﻿using Kimd.Common;
using Kimd.Logic;
using Kimd.Machine;
using Kimd.Motion;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using WorkStation.Common;
using static System.Windows.Forms.VisualStyles.VisualStyleElement;

namespace WorkStation.Logic
{
    //名称 Arm{headerIndex + 1}模组
    public class TransferLogic : FlowBase
    {
        /// <summary>
        /// 0 1 2 3   0,1操作面左右两个模组，  2,3背面两个
        /// </summary>
        private int _headerIndex;
        private Axis _z;
        private Axis _y;
        private Axis _r;
        private Axis _x;
        private List<Cylinder> _feedCylinders;
        private List<Cylinder> _feedVacs;
        private List<Cylinder> _feedRevVacs;
        private List<Cylinder> _recycleCylinders;
        private List<Cylinder> _recycleVacs;
        private List<Cylinder> _recycleRevVacs;
        private List<Sensor> _feedVacSenses;
        private List<Sensor> _recycleVacSenses;
        private Cylinder ChangeCylinder { get; set; }
        private List<Cylinder> _vacBreakTesterCylinder;
        //private List<Cylinder> _precisionUpFeedVacs;
        //private List<Cylinder> _precisionDownFeedVacs;
        private TransferStatus _status;
        private Sensor _upInOrigin;
        private Sensor _downInOrigin;
        private Sensor _ngInOrigin;
        private Sensor _upInMove;
        private Sensor _downInMove;
        private Sensor _ngInMove;

        private Tray _feedHeadTray;
        private Tray _recycleHeadTray;

        private Cylinder _precisionPositionUp;
        private Cylinder _precisionPositionDown;
        public TransferLogic(string flowName, FlowManager flowmanager, int headerIndex)
            : base(flowName, flowmanager)
        {
            _headerIndex = headerIndex;
            _z = MachineResource.Instance.MachineAxis[$"Arm{headerIndex + 1}Z轴"];
            _y = MachineResource.Instance.MachineAxis[$"Arm{headerIndex + 1}Y轴"];
            _r = MachineResource.Instance.MachineAxis[$"Arm{headerIndex + 1}U轴"];
            if (_headerIndex == 0)
                _x = MachineResource.Instance.MachineAxis[$"测试移载线1左轴"];
            if (_headerIndex == 1)
                _x = MachineResource.Instance.MachineAxis[$"测试移载线1右轴"];
            if (_headerIndex == 2)
                _x = MachineResource.Instance.MachineAxis[$"测试移载线2左轴"];
            if (_headerIndex == 3)
                _x = MachineResource.Instance.MachineAxis[$"测试移载线2右轴"];

            if (_headerIndex == 2)
            {
                this._feedCylinders = new List<Cylinder>()
                {
                    MachineResource.Instance.MachineCylinder[$"Arm{headerIndex + 1}上下气缸3"],
                    MachineResource.Instance.MachineCylinder[$"Arm{headerIndex + 1}上下气缸4"],
                };
                this._feedVacs = new List<Cylinder>()
                {
                    MachineResource.Instance.MachineCylinder[$"Arm{headerIndex + 1}吸真空3"],
                    MachineResource.Instance.MachineCylinder[$"Arm{headerIndex + 1}吸真空4"],
                };
                this._feedRevVacs = new List<Cylinder>()
                {
                    MachineResource.Instance.MachineCylinder[$"Arm{headerIndex + 1}破真空3"],
                    MachineResource.Instance.MachineCylinder[$"Arm{headerIndex + 1}破真空4"],
                };
                this._feedVacSenses = new List<Sensor>()
                {
                    MachineResource.Instance.MachineSensor[$"Arm{headerIndex + 1}吸真空3达到"],
                    MachineResource.Instance.MachineSensor[$"Arm{headerIndex + 1}吸真空4达到"],
                };
                this._recycleCylinders = new List<Cylinder>()
                {
                    MachineResource.Instance.MachineCylinder[$"Arm{headerIndex + 1}上下气缸1"],
                    MachineResource.Instance.MachineCylinder[$"Arm{headerIndex + 1}上下气缸2"],
                };
                this._recycleVacs = new List<Cylinder>()
                {
                    MachineResource.Instance.MachineCylinder[$"Arm{headerIndex + 1}吸真空1"],
                    MachineResource.Instance.MachineCylinder[$"Arm{headerIndex + 1}吸真空2"],
                };
                this._recycleRevVacs = new List<Cylinder>()
                {
                    MachineResource.Instance.MachineCylinder[$"Arm{headerIndex + 1}破真空1"],
                    MachineResource.Instance.MachineCylinder[$"Arm{headerIndex + 1}破真空2"],
                };
                this._recycleVacSenses = new List<Sensor>()
                {
                    MachineResource.Instance.MachineSensor[$"Arm{headerIndex + 1}吸真空1达到"],
                    MachineResource.Instance.MachineSensor[$"Arm{headerIndex + 1}吸真空2达到"],
                };
            }
            else
            {
                this._feedCylinders = new List<Cylinder>()
                {
                    MachineResource.Instance.MachineCylinder[$"Arm{headerIndex + 1}上下气缸1"],
                    MachineResource.Instance.MachineCylinder[$"Arm{headerIndex + 1}上下气缸2"],
                };
                this._feedVacs = new List<Cylinder>()
                {
                    MachineResource.Instance.MachineCylinder[$"Arm{headerIndex + 1}吸真空1"],
                    MachineResource.Instance.MachineCylinder[$"Arm{headerIndex + 1}吸真空2"],
                };
                this._feedRevVacs = new List<Cylinder>()
                {
                    MachineResource.Instance.MachineCylinder[$"Arm{headerIndex + 1}破真空1"],
                    MachineResource.Instance.MachineCylinder[$"Arm{headerIndex + 1}破真空2"],
                };
                this._feedVacSenses = new List<Sensor>()
                {
                    MachineResource.Instance.MachineSensor[$"Arm{headerIndex + 1}吸真空1达到"],
                    MachineResource.Instance.MachineSensor[$"Arm{headerIndex + 1}吸真空2达到"],
                };
                this._recycleCylinders = new List<Cylinder>()
                {
                    MachineResource.Instance.MachineCylinder[$"Arm{headerIndex + 1}上下气缸3"],
                    MachineResource.Instance.MachineCylinder[$"Arm{headerIndex + 1}上下气缸4"],
                };
                this._recycleVacs = new List<Cylinder>()
                {
                    MachineResource.Instance.MachineCylinder[$"Arm{headerIndex + 1}吸真空3"],
                    MachineResource.Instance.MachineCylinder[$"Arm{headerIndex + 1}吸真空4"],
                };
                this._recycleRevVacs = new List<Cylinder>()
                {
                    MachineResource.Instance.MachineCylinder[$"Arm{headerIndex + 1}破真空3"],
                    MachineResource.Instance.MachineCylinder[$"Arm{headerIndex + 1}破真空4"],
                };
                this._recycleVacSenses = new List<Sensor>()
                {
                    MachineResource.Instance.MachineSensor[$"Arm{headerIndex + 1}吸真空3达到"],
                    MachineResource.Instance.MachineSensor[$"Arm{headerIndex + 1}吸真空4达到"],
                };
            }
            this._vacBreakTesterCylinder = new List<Cylinder>()
            {
               MachineResource.Instance.MachineCylinder[$"Arm{headerIndex + 1}治具1吹"],
               MachineResource.Instance.MachineCylinder[$"Arm{headerIndex + 1}治具2吹"],
               MachineResource.Instance.MachineCylinder[$"Arm{headerIndex + 1}治具3吹"],
            };

            if (headerIndex < 2)
            {
                this._upInOrigin = MachineResource.Instance.MachineSensor["精定位1上层移载气缸原点"];
                this._upInMove = MachineResource.Instance.MachineSensor["精定位1上层移载气缸动点"];
                this._downInOrigin = MachineResource.Instance.MachineSensor["精定位1下层移载气缸原点"];
                this._downInMove = MachineResource.Instance.MachineSensor["精定位1下层移载气缸动点"];
                this._ngInOrigin = MachineResource.Instance.MachineSensor["精定位1ANG盘在原点位"];
                this._ngInMove = MachineResource.Instance.MachineSensor["精定位1NG料移载气缸动点"];
                //if (_headerIndex == 0)
                //{
                //    this._precisionUpFeedVacs = new List<Cylinder>()
                //    {
                //        MachineResource.Instance.MachineCylinder["精定位1上层真空吸5"],
                //        MachineResource.Instance.MachineCylinder["精定位1上层真空吸6"],
                //    };
                //    this._precisionDownFeedVacs = new List<Cylinder>()
                //    {
                //        MachineResource.Instance.MachineCylinder["精定位1下层真空吸5"],
                //        MachineResource.Instance.MachineCylinder["精定位1下层真空吸6"],
                //    };
                //}
                //else
                //{
                //    this._precisionUpFeedVacs = new List<Cylinder>()
                //    {
                //        MachineResource.Instance.MachineCylinder["精定位1上层真空吸1"],
                //        MachineResource.Instance.MachineCylinder["精定位1上层真空吸2"],
                //    };
                //    this._precisionDownFeedVacs = new List<Cylinder>()
                //    {
                //        MachineResource.Instance.MachineCylinder["精定位1下层真空吸1"],
                //        MachineResource.Instance.MachineCylinder["精定位1下层真空吸2"],
                //    };
                //}
            }
            else
            {
                this._upInOrigin = MachineResource.Instance.MachineSensor["精定位2上层移载气缸原点"];
                this._upInMove = MachineResource.Instance.MachineSensor["精定位2上层移载气缸动点"];
                this._downInOrigin = MachineResource.Instance.MachineSensor["精定位2下层移载气缸原点"];
                this._downInMove = MachineResource.Instance.MachineSensor["精定位2下层移载气缸动点"];
                this._ngInOrigin = MachineResource.Instance.MachineSensor["精定位2ANG盘在原点位"];
                this._ngInMove = MachineResource.Instance.MachineSensor["精定位2NG料移载气缸动点"];
                //if (_headerIndex == 0)
                //{
                //    this._precisionUpFeedVacs = new List<Cylinder>()
                //    {
                //        MachineResource.Instance.MachineCylinder["精定位2上层真空吸5"],
                //        MachineResource.Instance.MachineCylinder["精定位2上层真空吸6"],
                //    };
                //    this._precisionDownFeedVacs = new List<Cylinder>()
                //    {
                //        MachineResource.Instance.MachineCylinder["精定位2下层真空吸5"],
                //        MachineResource.Instance.MachineCylinder["精定位2下层真空吸6"],
                //    };
                //}
                //else
                //{
                //    this._precisionUpFeedVacs = new List<Cylinder>()
                //    {
                //        MachineResource.Instance.MachineCylinder["精定位2上层真空吸1"],
                //        MachineResource.Instance.MachineCylinder["精定位2上层真空吸2"],
                //    };
                //    this._precisionDownFeedVacs = new List<Cylinder>()
                //    {
                //        MachineResource.Instance.MachineCylinder["精定位2下层真空吸1"],
                //        MachineResource.Instance.MachineCylinder["精定位2下层真空吸2"],
                //    };
                //}
            }
            if (_headerIndex == 0)
            {
                _precisionPositionUp = MachineResource.Instance.MachineCylinder["精定位1上层定位气缸2-2"];
                _precisionPositionDown = MachineResource.Instance.MachineCylinder["精定位1下层定位气缸2-2"];
            }
            if (_headerIndex == 1)
            {
                _precisionPositionUp = MachineResource.Instance.MachineCylinder["精定位1上层定位气缸1-2"];
                _precisionPositionDown = MachineResource.Instance.MachineCylinder["精定位1下层定位气缸1-2"];
            }
            if (_headerIndex == 2)
            {
                _precisionPositionUp = MachineResource.Instance.MachineCylinder["精定位2上层定位气缸2-2"];
                _precisionPositionDown = MachineResource.Instance.MachineCylinder["精定位2下层定位气缸2-2"];
            }
            if (_headerIndex == 3)
            {
                _precisionPositionUp = MachineResource.Instance.MachineCylinder["精定位2上层定位气缸1-2"];
                _precisionPositionDown = MachineResource.Instance.MachineCylinder["精定位2下层定位气缸1-2"];
            }

            RegisterDirectSignal("回零完成");
            RegisterDirectSignal($"测试治具{_headerIndex + 1}1开始测试");
            RegisterDirectSignal($"测试治具{_headerIndex + 1}2开始测试");
            RegisterDirectSignal($"测试治具{_headerIndex + 1}3开始测试");
            RegisterDirectSignal($"Arm{_headerIndex + 1}准备放完成料");
            RegisterDirectSignal($"Arm{_headerIndex + 1}取精定位完成");
            RegisterDirectSignal($"取测试模组{_headerIndex + 1}1完成");
            RegisterDirectSignal($"取测试模组{_headerIndex + 1}2完成");
            RegisterDirectSignal($"取测试模组{_headerIndex + 1}3完成");
            RegisterDirectSignal($"Arm{_headerIndex + 1}放扫码NG请求");
            this.ChangeCylinder = MachineResource.Instance.MachineCylinder[$"Arm{headerIndex + 1}变距气缸"];
            _feedHeadTray = new Tray(2, 1);
            _recycleHeadTray = new Tray(2, 1);
        }

        public override void Calibrate()
        {
            base.Calibrate();
        }

        public override void Home()
        {
            initAxis();
            MultiCylinderOperate(_feedCylinders, 5000, new List<bool>() { false, false });
            if (!CheckFlowCondition()) return;
            MultiCylinderOperate(_recycleCylinders, 5000, new List<bool>() { false, false });
            if (!CheckFlowCondition()) return;
            SingleCylinderOperate(ChangeCylinder, 5000, false, isNeedLog: true);
            if (!CheckFlowCondition()) return;
            _feedRevVacs.ForEach(v => v.Off());
            _recycleRevVacs.ForEach(v => v.Off());
            _vacBreakTesterCylinder.ForEach(v => v.Off());
            if (!GlobalVariable.IsSystemAlreadyHome)
            {
                SingleAxisGoHome(_z);
                if (!CheckFlowCondition()) return;
                SingleAxisGoHome(_x);
                if (!CheckFlowCondition()) return;
                SingleAxisGoHome(_y);
                if (!CheckFlowCondition()) return;
                SingleAxisGoHome(_r);
                if (!CheckFlowCondition()) return;
            }
            else
            {
                SingleAxisMove2Point(_z, "待机位", false, SpeedType.Manual);
                if (!CheckFlowCondition()) return;
                SingleAxisMove2Point(_y, "旋转位", false, SpeedType.Manual);
                if (!CheckFlowCondition()) return;
                SingleAxisMove2Point(_r, "待机位", false, SpeedType.Manual);
                if (!CheckFlowCondition()) return;
                SingleAxisMove2Point(_x, "待机位", false, SpeedType.Manual);
                if (!CheckFlowCondition()) return;
            }
            //SingleAxisMove2Point(_r, "回零偏移");
            //if (!CheckFlowCondition()) return;
            //_r.Zero();
            Thread.Sleep(200);
            AxisMoveSimultaneously(new List<Axis>() { _x, _y, _r }, "待机位", false, SpeedType.Manual);
            if (!CheckFlowCondition()) return;

            _feedHeadTray.Helper.NewEmptyTray();
            _recycleHeadTray.Helper.NewEmptyTray();

            checkProductStatus();

            SetSignal("回零完成");
            _status = TransferStatus.TwoPositionNoProduct;

            GlobalVariable.ClearNG[_headerIndex] = false;
        }

        public override void Init(out string errInfo)
        {
            base.Init(out errInfo);
        }

        public override void Method()
        {
            if (!CheckFlowCondition()) return;
            if (GlobalVariable.IsSpotChecking)
                return;
            switch (_status)
            {
                //上下料都没有料
                case TransferStatus.TwoPositionNoProduct:
                    //SingleAxisMove2Point(_z, "待机位");
                    if (!CheckFlowCondition()) return;
                    MachineMessageScheduler.Instance.Add(new MachineMessage(MessageType.Log, $"{this.Description}进入TwoPositionNoProduct状态", this.Description, 0, ""));
                    bool upReady = false;
                    Tray feedTray = null;
                    if (_headerIndex < 2)
                    {
                        WaitForSignal("精定位移载1", $"Arm{_headerIndex + 1}精定位到位");
                        if (!CheckFlowCondition()) return;
                        upReady = (_flowManager.SelectFlow("精定位移载1") as PrecisionLogic).GetTwoLayerInPositionStatus();
                        MachineMessageScheduler.Instance.Add(new MachineMessage(MessageType.Log, $"当前上下层状态：{upReady}", this.Description, 0, ""));
                        if (_headerIndex % 2 == 0)
                        {
                            feedTray = upReady ? (_flowManager.SelectFlow("精定位移载1") as PrecisionLogic).GetFeedTray1Up() : (_flowManager.SelectFlow("精定位移载1") as PrecisionLogic).GetFeedTray1Down();
                        }
                        else
                        {
                            feedTray = upReady ? (_flowManager.SelectFlow("精定位移载1") as PrecisionLogic).GetFeedTray2Up() : (_flowManager.SelectFlow("精定位移载1") as PrecisionLogic).GetFeedTray2Down();
                        }
                    }
                    else
                    {
                        WaitForSignal("精定位移载2", $"Arm{_headerIndex + 1}精定位到位");
                        if (!CheckFlowCondition()) return;
                        upReady = (_flowManager.SelectFlow("精定位移载2") as PrecisionLogic).GetTwoLayerInPositionStatus();
                        MachineMessageScheduler.Instance.Add(new MachineMessage(MessageType.Log, $"当前上下层状态：{upReady}", this.Description, 0, ""));
                        if (_headerIndex % 2 == 0)
                        {
                            feedTray = upReady ? (_flowManager.SelectFlow("精定位移载2") as PrecisionLogic).GetFeedTray1Up() : (_flowManager.SelectFlow("精定位移载2") as PrecisionLogic).GetFeedTray1Down();
                        }
                        else
                        {
                            feedTray = upReady ? (_flowManager.SelectFlow("精定位移载2") as PrecisionLogic).GetFeedTray2Up() : (_flowManager.SelectFlow("精定位移载2") as PrecisionLogic).GetFeedTray2Down();
                        }
                    }
                    bool isGet = false;
                    //有料才去真正取料，但是信号还是会发
                    if ((feedTray.Helper.GetCell(0).Status == CellStatus.HasProduct ||
                         feedTray.Helper.GetCell(1).Status == CellStatus.HasProduct))
                    {
                        MachineMessageScheduler.Instance.Add(new MachineMessage(MessageType.Log, $"精定位有待测料可取", this.Description, 0, ""));
                        //确保上下层在正确的位置后，方可下Z
                        safetyCheck(upReady);
                        if (!CheckFlowCondition()) return;
                        //如果有扫码,1头不允许的是2头是否在放扫码NG，2头不允许1头在放扫码NG
                        LogHelper.Instance.Log(this.Description, "取待测料开始", MessageLevel.CT);
                        getFeedProduct(feedTray, upReady);
                        LogHelper.Instance.Log(this.Description, "取待测料结束", MessageLevel.CT);
                        isGet = true;
                    }
                    else
                    {
                        MachineMessageScheduler.Instance.Add(new MachineMessage(MessageType.Log, $"精定位没有待测料可取", this.Description, 0, ""));
                    }
                    SetSignal($"Arm{_headerIndex + 1}取精定位完成");
                    if (!CheckFlowCondition()) return;
                    //增加外部扫码动作
                    if (GlobalVariable.ChangeScanPosition && isGet && !GlobalVariable.IsDryRun)
                    {
                        if (_feedHeadTray.Helper.GetCell(0).Status == CellStatus.HasProduct ||
                            _feedHeadTray.Helper.GetCell(1).Status == CellStatus.HasProduct)
                        {
                            MachineMessageScheduler.Instance.Add(new MachineMessage(MessageType.Log, $"开始外部扫码动作", this.Description, 0, _x.Module));
                            SingleAxisMove2Point(_z, "待机位");
                            if (!CheckFlowCondition()) return;
                            if (SystemConfig.Instance.UsingStatus.IsScannerInUse)
                            {
                                CTMoveStartCalculateNode();
                                SingleAxisMove2Point(_y, "旋转位");
                                if (!CheckFlowCondition()) return;
                                AxisMoveSimultaneously(_x, _r, $"扫码位置1");
                                if (!CheckFlowCondition()) return;
                                CTMoveStartCalculateNode();
                                SingleAxisMove2Point(_y, $"扫码位置1");
                                CTMoveEndCalculateNode(out double time);
                                MachineMessageScheduler.Instance.Add(new MachineMessage(MessageType.CT, time.ToString("0.0"), "", 0, $"Arm{_headerIndex + 1}移动到扫码位"));
                                CTMoveStartCalculateNode();
                                if (!CheckFlowCondition()) return;
                                if (_feedHeadTray.Helper.GetCell(0).Status == CellStatus.HasProduct)
                                    _feedCylinders[0].On();
                                if (_feedHeadTray.Helper.GetCell(1).Status == CellStatus.HasProduct)
                                    _feedCylinders[1].On();
                                if (!CheckFlowCondition()) return;
                                SingleAxisMove2Point(_z, $"扫码位置1");
                                if (!CheckFlowCondition()) return;
                                if (_feedHeadTray.Helper.GetCell(0).Status == CellStatus.HasProduct)
                                    SingleCylinderOperate(_feedCylinders[0], 5000, true);
                                if (!CheckFlowCondition()) return;
                                if (_feedHeadTray.Helper.GetCell(1).Status == CellStatus.HasProduct)
                                    SingleCylinderOperate(_feedCylinders[1], 5000, true);
                                if (!CheckFlowCondition()) return;
                                SingleAxisMove2Point(_y, $"扫码位置1拨Flex位");
                                Thread.Sleep(SystemConfig.Instance.UsingStatus.ScanDelay);
                            }
                            if (!CheckFlowCondition()) return;
                            if (GlobalVariable.IsGrrMode)
                            {
                                if (_feedHeadTray.Helper.GetCell(0).Status == CellStatus.HasProduct)
                                {
                                    PrecisionLogic logic = null;
                                    string barcode = "";
                                    if (_headerIndex < 2)
                                    {
                                        //精定位移载1
                                        logic = _flowManager.SelectFlow($"精定位移载1") as PrecisionLogic;
                                        barcode = logic.ScanBarcode(_headerIndex == 0 ? logic.BarcodeScanner1 : logic.BarcodeScanner3, 0);
                                    }
                                    else
                                    {
                                        logic = _flowManager.SelectFlow($"精定位移载2") as PrecisionLogic;
                                        barcode = logic.ScanBarcode(_headerIndex == 2 ? logic.BarcodeScanner1 : logic.BarcodeScanner3, 0);
                                    }
                                    MachineMessageScheduler.Instance.Add(new MachineMessage(MessageType.Log, $"第1个产品进行扫码，结果为:{barcode}", this.Description, 0, _x.Module));
                                    _feedHeadTray.Helper.SetCellBarcode(0, barcode);
                                }

                                if (_feedHeadTray.Helper.GetCell(1).Status == CellStatus.HasProduct)
                                {
                                    PrecisionLogic logic = null;
                                    string barcode = "";
                                    if (_headerIndex < 2)
                                    {
                                        logic = _flowManager.SelectFlow($"精定位移载1") as PrecisionLogic;
                                        barcode = logic.ScanBarcode(_headerIndex == 0 ? logic.BarcodeScanner2 : logic.BarcodeScanner4, 1);
                                    }
                                    else
                                    {
                                        logic = _flowManager.SelectFlow($"精定位移载2") as PrecisionLogic;
                                        barcode = logic.ScanBarcode(_headerIndex == 2 ? logic.BarcodeScanner2 : logic.BarcodeScanner4, 1);
                                    }
                                    MachineMessageScheduler.Instance.Add(new MachineMessage(MessageType.Log, $"第2个产品进行扫码，结果为:{barcode}", this.Description, 0, _x.Module));
                                    _feedHeadTray.Helper.SetCellBarcode(1, barcode);
                                }
                            }
                            else
                            {
                                Task taskBarcode1 = Task.Run(() =>
                                {
                                    if (_feedHeadTray.Helper.GetCell(0).Status == CellStatus.HasProduct)
                                    {
                                        PrecisionLogic logic = null;
                                        string barcode = "";
                                        if (_headerIndex < 2)
                                        {
                                            //精定位移载1
                                            logic = _flowManager.SelectFlow($"精定位移载1") as PrecisionLogic;
                                            barcode = logic.ScanBarcode(_headerIndex == 0 ? logic.BarcodeScanner1 : logic.BarcodeScanner3, 0);
                                        }
                                        else
                                        {
                                            logic = _flowManager.SelectFlow($"精定位移载2") as PrecisionLogic;
                                            barcode = logic.ScanBarcode(_headerIndex == 2 ? logic.BarcodeScanner1 : logic.BarcodeScanner3, 0);
                                        }
                                        MachineMessageScheduler.Instance.Add(new MachineMessage(MessageType.Log, $"第1个产品进行扫码，结果为:{barcode}", this.Description, 0, _x.Module));
                                        _feedHeadTray.Helper.SetCellBarcode(0, barcode);
                                    }

                                });
                                Task taskBarcode2 = Task.Run(() =>
                                {
                                    if (_feedHeadTray.Helper.GetCell(1).Status == CellStatus.HasProduct)
                                    {
                                        PrecisionLogic logic = null;
                                        string barcode = "";
                                        if (_headerIndex < 2)
                                        {
                                            logic = _flowManager.SelectFlow($"精定位移载1") as PrecisionLogic;
                                            barcode = logic.ScanBarcode(_headerIndex == 0 ? logic.BarcodeScanner2 : logic.BarcodeScanner4, 1);
                                        }
                                        else
                                        {
                                            logic = _flowManager.SelectFlow($"精定位移载2") as PrecisionLogic;
                                            barcode = logic.ScanBarcode(_headerIndex == 2 ? logic.BarcodeScanner2 : logic.BarcodeScanner4, 1);
                                        }
                                        MachineMessageScheduler.Instance.Add(new MachineMessage(MessageType.Log, $"第2个产品进行扫码，结果为:{barcode}", this.Description, 0, _x.Module));
                                        _feedHeadTray.Helper.SetCellBarcode(1, barcode);
                                    }
                                });
                                Task.WaitAll(taskBarcode1, taskBarcode2);
                            }
                            SingleAxisMove2Point(_y, $"扫码位置1");
                            if (!CheckFlowCondition()) return;
                            if (_feedHeadTray.Helper.GetCell(0).Status == CellStatus.HasProduct)
                                _feedCylinders[0].Off();
                            if (_feedHeadTray.Helper.GetCell(1).Status == CellStatus.HasProduct)
                                _feedCylinders[1].Off();
                            SingleAxisMove2Point(_z, "待机位");
                            if (!CheckFlowCondition()) return;
                            if (_feedHeadTray.Helper.GetCell(0).Status == CellStatus.HasProduct)
                                SingleCylinderOperate(_feedCylinders[0], 5000, false);
                            if (!CheckFlowCondition()) return;
                            if (_feedHeadTray.Helper.GetCell(1).Status == CellStatus.HasProduct)
                                SingleCylinderOperate(_feedCylinders[1], 5000, false);
                            if (!CheckFlowCondition()) return;
                            CTMoveEndCalculateNode(out double time1);
                            MachineMessageScheduler.Instance.Add(new MachineMessage(MessageType.CT, time1.ToString("0.0"), "", 0, $"Arm{_headerIndex + 1}扫码"));

                        }
                    }
                    else if (isGet && GlobalVariable.IsDryRun)
                    {
                        if (_feedHeadTray.Helper.GetCell(0).Status == CellStatus.HasProduct)
                            _feedHeadTray.Helper.SetCellBarcode(0, $"{DateTime.Now.ToString("yyyyMMddHHmmssfff")}-{_headerIndex + 1}1");
                        if (_feedHeadTray.Helper.GetCell(1).Status == CellStatus.HasProduct)
                            _feedHeadTray.Helper.SetCellBarcode(1, $"{DateTime.Now.ToString("yyyyMMddHHmmssfff")}-{_headerIndex + 1}2");
                    }
                    if (!CheckFlowCondition()) return;
                    SetSignal($"Arm{_headerIndex + 1}放扫码NG请求");
                    if (_headerIndex < 2)
                    {
                        WaitForSignal("前放扫码NG模组", $"Arm{_headerIndex + 1}放扫码NG完成");
                        if (!CheckFlowCondition()) return;
                    }
                    else
                    {
                        WaitForSignal("后放扫码NG模组", $"Arm{_headerIndex + 1}放扫码NG完成");
                        if (!CheckFlowCondition()) return;
                    }
                    if (!CheckFlowCondition()) return;
                    _status = TransferStatus.FeedHasProduct;
                    MachineMessageScheduler.Instance.Add(new MachineMessage(MessageType.Log, $"{this.Description}即将进入FeedHasProduct状态", this.Description, 0, ""));
                    break;

                case TransferStatus.FeedHasProduct:
                    //MachineMessageScheduler.Instance.Add(new MachineMessage(MessageType.Log, $"{this.Description}进入FeedHasProduct状态", this.Description, 0, ""));
                    //加一步判断是否所有tester都被取完
                    if (GlobalVariable.IsGrrMode)
                    {
                        if (_recycleHeadTray.Helper.Any(c => c.Status == CellStatus.HasProduct))
                        {
                            _status = TransferStatus.RecycleHasProduct;
                            break;
                        }
                    }
                    bool hasProductInFeedHand = _feedHeadTray.Helper.Any(c => c.Status == CellStatus.HasProduct);
                    string trackName = _headerIndex < 2 ? $"轨道拍照1" : $"轨道拍照2";
                    //治具没料的时候且持续上料的时候启用
                    if (!hasProductInFeedHand &&
                        CommonMethod.AllTesterNoProduct(_headerIndex < 2 ? 0 : 1) &&
                        CommonMethod.AllArmNoRecycleProduct(_headerIndex < 2 ? 0 : 1) &&
                        CommonMethod.AllArmNoProduct(_headerIndex < 2 ? 0 : 1) &&
                        (!(_flowManager.SelectFlow(trackName) as CaptureTrayLogic).GetFeedTray().Helper.IsEmpty) &&
                        CommonMethod.AllPrecisionNoProduct(_headerIndex < 2 ? 0 : 1))
                    {
                        //GlobalVariable.ArmIdle[_headerIndex] = true;
                        MachineMessageScheduler.Instance.Add(new MachineMessage(MessageType.Log, $"手上没有取到待测料 且 所有治具都被取空了，进入TwoPositionNoProduct状态", this.Description, 0, ""));
                        _status = TransferStatus.TwoPositionNoProduct;
                    }
                    else
                    {
                        int idleFinishTesterIndex = -1;
                        //在线复测模式下，处于AB复测的料，需要找到属于自己的治具
                        if (_feedHeadTray.Helper.Any(c => c.Result == TestResult.SecondTimeNG))
                        {
                            int lastTesterIndex = _feedHeadTray.Helper.GetCell(0).TestedTesters.Last();
                            MachineMessageScheduler.Instance.Add(new MachineMessage(MessageType.Log, $"手上有AB NG的料，等待治具{lastTesterIndex + 1}完成再上料", this.Description, 0, ""));
                            while (!IsBreak() && !IsTesterIdleOrFinish(_headerIndex, lastTesterIndex))
                            {
                                Thread.Sleep(20);
                            }
                            if (!CheckFlowCondition()) return;
                            idleFinishTesterIndex = lastTesterIndex;
                        }
                        else
                        {
                            if (GlobalVariable.IsCosmeticMode)
                            {
                                idleFinishTesterIndex = SelectIdleTester();
                                if (idleFinishTesterIndex == -1)
                                {
                                    idleFinishTesterIndex = SelectFinishTester();
                                }
                                else
                                {
                                    for (int i = 0; i < GlobalVariable.CosmeticFinishTesterIndex.Count; i++)
                                    {
                                        if (GlobalVariable.CosmeticFinishTesterIndex[i] % 6 == idleFinishTesterIndex)
                                        {
                                            idleFinishTesterIndex = SelectFinishTester();
                                            break;
                                        }
                                    }
                                    //if(GlobalVariable.CosmeticFinishTesterIndex.Any(c=>c%6 == idleFinishTesterIndex))
                                    ////if(GlobalVariable.CosmeticFinishTesterIndex.Contains(idleFinishTesterIndex))
                                    //{
                                    //    idleFinishTesterIndex = SelectFinishTester(_headerIndex);
                                    //}
                                }
                            }
                            else
                                idleFinishTesterIndex = SelectIdleOrFinishTester(_headerIndex, hasProductInFeedHand);
                        }
                        //如果找到了可以放料的Tester
                        if (idleFinishTesterIndex != -1)
                        {
                            MachineMessageScheduler.Instance.Add(new MachineMessage(MessageType.Log, $"找到了治具{idleFinishTesterIndex + 1}，准备取放料", this.Description, 0, ""));
                            GoAndGetPutProduct(idleFinishTesterIndex);
                            if (!CheckFlowCondition()) return;
                            MachineMessageScheduler.Instance.Add(new MachineMessage(MessageType.Log, $"手上有测完的料，进入RecycleHasProduct状态", this.Description, 0, ""));
                            _status = TransferStatus.RecycleHasProduct;
                        }
                        else
                        {
                            if (false)
                            {
                                #region 两侧arm启用的治具不对等的情况下
                                if (_headerIndex < 2)
                                {
                                    if (SystemConfig.Instance.UsingStatus.TesterInUseStatus.Take(3).Count(t => t) != SystemConfig.Instance.UsingStatus.TesterInUseStatus.Skip(3).Take(3).Count(t => t))
                                    {
                                        //非清料和GRR等模式下
                                        if (!(GlobalVariable.IsClearLater || GlobalVariable.IsClearNow) && !GlobalVariable.IsGrrMode)
                                        {
                                            if (_headerIndex == 0)
                                            {
                                                if (CheckSignal("Arm2模组", $"Arm2准备放完成料"))
                                                {
                                                    MachineMessageScheduler.Instance.Add(new MachineMessage(MessageType.Log, $"{this.Description} 清料模式下，当前Arm已经结束所有动作了，但是另一边的Arm还要继续，让Arm进入RecycleHasProduct状态", this.Description, 0, ""));
                                                    _status = TransferStatus.RecycleHasProduct;
                                                }
                                            }
                                            else if (_headerIndex == 1)
                                            {
                                                if (CheckSignal("Arm1模组", $"Arm1准备放完成料"))
                                                {
                                                    MachineMessageScheduler.Instance.Add(new MachineMessage(MessageType.Log, $"{this.Description} 清料模式下，当前Arm已经结束所有动作了，但是另一边的Arm还要继续，让Arm进入RecycleHasProduct状态", this.Description, 0, ""));
                                                    _status = TransferStatus.RecycleHasProduct;
                                                }
                                            }
                                        }
                                    }
                                }
                                else
                                {
                                    if (SystemConfig.Instance.UsingStatus.TesterInUseStatus.Skip(6).Take(3).Count(t => t) != SystemConfig.Instance.UsingStatus.TesterInUseStatus.Skip(9).Take(3).Count(t => t))
                                    {
                                        if (!(GlobalVariable.IsClearLater2 || GlobalVariable.IsClearNow2) && !GlobalVariable.IsGrrMode)
                                        {
                                            if (_headerIndex == 2)
                                            {
                                                if (CheckSignal("Arm4模组", $"Arm4准备放完成料"))
                                                {
                                                    MachineMessageScheduler.Instance.Add(new MachineMessage(MessageType.Log, $"{this.Description} 清料模式下，当前Arm已经结束所有动作了，但是另一边的Arm还要继续，让Arm进入RecycleHasProduct状态", this.Description, 0, ""));
                                                    _status = TransferStatus.RecycleHasProduct;
                                                }
                                            }
                                            else if (_headerIndex == 3)
                                            {
                                                if (CheckSignal("Arm3模组", $"Arm3准备放完成料"))
                                                {
                                                    MachineMessageScheduler.Instance.Add(new MachineMessage(MessageType.Log, $"{this.Description} 清料模式下，当前Arm已经结束所有动作了，但是另一边的Arm还要继续，让Arm进入RecycleHasProduct状态", this.Description, 0, ""));
                                                    _status = TransferStatus.RecycleHasProduct;
                                                }
                                            }
                                        }
                                    }
                                }
                                #endregion
                            }
                        }
                        int index = 0;
                        if (_headerIndex == 0)
                            index = 1;
                        if (_headerIndex == 1)
                            index = 0;
                        if (_headerIndex == 2)
                            index = 3;
                        if (_headerIndex == 3)
                            index = 2;
                        //当前Arm已经结束所有动作了，但是另一边的Arm还要继续，让Arm进入RecycleHasProduct状态
                        if (CommonMethod.AllArmTesterNoProduct(_headerIndex) &&
                            CommonMethod.AllArmNoProductSingle(_headerIndex) &&
                            (CommonMethod.ArmRecycleHasProductSingle(index) || !CommonMethod.AllArmTesterNoProduct(index)))
                        {
                            MachineMessageScheduler.Instance.Add(new MachineMessage(MessageType.Log, $"{this.Description} 清料模式下，当前Arm已经结束所有动作了，但是另一边的Arm还要继续，让Arm进入RecycleHasProduct状态", this.Description, 0, ""));
                            _status = TransferStatus.RecycleHasProduct;
                        }
                    }
                    if (CommonMethod.AllArmNoProduct(_headerIndex < 2 ? 0 : 1) &&
                       CommonMethod.AllTesterNoProduct(_headerIndex < 2 ? 0 : 2))
                    {
                        //若处于清料模式下，要清空掉ANG,ABNG
                        if (_headerIndex < 2)
                        {
                            if ((GlobalVariable.IsClearLater || GlobalVariable.IsClearNow) && GlobalVariable.IsOnlineRetest)
                            {
                                if (CommonMethod.ANGHasProduct(_headerIndex < 2 ? 0 : 1) ||
                                    CommonMethod.ABNGHasProduct(_headerIndex < 2 ? 0 : 1))
                                {
                                    GlobalVariable.ClearNG[_headerIndex] = true;
                                    MachineMessageScheduler.Instance.Add(new MachineMessage(MessageType.Log, $"手上和治具上都没有料，复测&清料模式下，清NG", this.Description, 0, ""));
                                    _status = TransferStatus.RecycleHasProduct;
                                }
                                else
                                {
                                    MachineMessageScheduler.Instance.Add(new MachineMessage(MessageType.Log, $"NG清完了，进入TwoPositionNoProduct状态", this.Description, 0, ""));
                                    _status = TransferStatus.TwoPositionNoProduct;
                                }
                            }
                            else
                            {
                                MachineMessageScheduler.Instance.Add(new MachineMessage(MessageType.Log, $"手上和治具上都没有料，进入TwoPositionNoProduct状态", this.Description, 0, ""));
                                _status = TransferStatus.TwoPositionNoProduct;
                            }
                        }
                        else
                        {
                            if ((GlobalVariable.IsClearLater2 || GlobalVariable.IsClearNow2) && GlobalVariable.IsOnlineRetest)
                            {
                                if (CommonMethod.ANGHasProduct(_headerIndex < 2 ? 0 : 1) ||
                                    CommonMethod.ABNGHasProduct(_headerIndex < 2 ? 0 : 1))
                                {
                                    GlobalVariable.ClearNG[_headerIndex] = true;
                                    MachineMessageScheduler.Instance.Add(new MachineMessage(MessageType.Log, $"手上和治具上都没有料，复测&清料模式下，清NG", this.Description, 0, ""));
                                    _status = TransferStatus.RecycleHasProduct;
                                }
                                else
                                {
                                    MachineMessageScheduler.Instance.Add(new MachineMessage(MessageType.Log, $"NG清完了，进入TwoPositionNoProduct状态", this.Description, 0, ""));
                                    _status = TransferStatus.TwoPositionNoProduct;
                                }
                            }
                            else
                            {
                                MachineMessageScheduler.Instance.Add(new MachineMessage(MessageType.Log, $"手上和治具上都没有料，进入TwoPositionNoProduct状态", this.Description, 0, ""));
                                _status = TransferStatus.TwoPositionNoProduct;
                            }
                        }
                    }
                    break;

                case TransferStatus.RecycleHasProduct:
                    MachineMessageScheduler.Instance.Add(new MachineMessage(MessageType.Log, $"{this.Description}进入RecycleHasProduct状态", this.Description, 0, ""));
                    SingleAxisMove2Point(_z, "待机位");
                    if (!CheckFlowCondition()) return;
                    ChangeCylinder.Off();
                    MachineMessageScheduler.Instance.Add(new MachineMessage(MessageType.Log, $"{ChangeCylinder.Description}--{ChangeCylinder._bitNo1}", this.Description, 0, ""));
                    AxisMoveSimultaneously(new List<Axis>() { _x, _y, _r }, "待机位");
                    SingleCylinderOperate(ChangeCylinder, 5000, false, isNeedLog: true);
                    if (!CheckFlowCondition()) return;
                    SetSignal($"Arm{_headerIndex + 1}准备放完成料");
                    if (_headerIndex < 2)
                    {
                        WaitForSignal("前Arm模组", $"Arm{_headerIndex + 1}放料结束");
                        if (!CheckFlowCondition()) return;
                    }
                    else
                    {
                        WaitForSignal("后Arm模组", $"Arm{_headerIndex + 1}放料结束");
                        if (!CheckFlowCondition()) return;
                    }
                    _status = TransferStatus.TwoPositionNoProduct;

                    break;
            }
        }
        public override void OnStop()
        {
            base.OnStop();
        }
        public override bool PreStartCheck()
        {
            return base.PreStartCheck();
        }
        public Tray GetFeedTray()
        {
            return _feedHeadTray;
        }
        public Tray GetRecycleTray()
        {
            return _recycleHeadTray;
        }
        public void initAxis()
        {
            _x.ClearAlarm();
            _x.SetServoOn();
            _y.ClearAlarm();
            _y.SetServoOn();
            _z.ClearAlarm();
            _z.SetServoOn();
            _r.ClearAlarm();
            _r.SetServoOn();
        }
        public void checkProductStatus()
        {
        restart:
            for (int i = 0; i < _feedVacSenses.Count; i++)
            {
                if (_feedVacSenses[i].WaitOn())
                {
                    MachineMessageScheduler.Instance.Add(new MachineMessage(MessageType.InfoAndPause, $"Arm{_headerIndex + 1}手上有料，请取走", this.Description, 0, "系统"));
                    //出错，停下所有线程
                    _flowManager.Pause();
                    //若为报错，需重新检测启动信号
                    WaitForStart();
                    if (!CheckFlowCondition()) return;
                    goto restart;
                }
            }
            //关闭所有真空
            _feedVacs.ForEach(v => v.Off());

            for (int i = 0; i < _recycleVacSenses.Count; i++)
            {
                if (_recycleVacSenses[i].WaitOn())
                {
                    MachineMessageScheduler.Instance.Add(new MachineMessage(MessageType.InfoAndPause, $"Arm{_headerIndex + 1}手上有料，请取走", this.Description, 0, "系统"));
                    //出错，停下所有线程
                    _flowManager.Pause();
                    //若为报错，需重新检测启动信号
                    WaitForStart();
                    if (!CheckFlowCondition()) return;
                    goto restart;
                }
            }
            //关闭所有真空
            _recycleVacs.ForEach(v => v.Off());
        }
        public void safetyCheck(bool upReady)
        {
            if (!isSimulate)
            {
                while (!_ngInOrigin.WaitOn())
                {
                    ShowWarnAndPause("NG料层不在安全位置", _x.Module);
                    Thread.Sleep(20);
                }
                //while (_ngInMove.WaitOn())
                //{
                //    ShowWarnAndPause("NG料层不在安全位置", _x.Module);
                //    Thread.Sleep(20);
                //}
                if (!upReady)
                {
                    while (!_upInMove.WaitOn())
                    {
                        ShowWarnAndPause("精定位上层不在安全位置", _x.Module);
                        Thread.Sleep(20);
                    }
                    while (_upInOrigin.WaitOn())
                    {
                        ShowWarnAndPause("精定位上层不在安全位置", _x.Module);
                        Thread.Sleep(20);
                    }
                }
            }
        }
        public void getFeedProduct(Tray feedTray, bool upReady)
        {
            //确保手上没有待测料，才去取，否则可能是复测，不需要取
            if (!(_feedHeadTray.Helper.Any(c => c.Status == CellStatus.HasProduct)))
            {
                MachineMessageScheduler.Instance.Add(new MachineMessage(MessageType.Log, $"手上原先一颗料都没有，开始取精定位", this.Description, 0, ""));
                CTMoveStartCalculateNode();
                string getPos = upReady ? $"Arm{_headerIndex + 1}精定位上层取料位置" : $"Arm{_headerIndex + 1}精定位下层取料位置";
                string localName = upReady ? $"Arm{_headerIndex + 1}精定位上层" : $"Arm{_headerIndex + 1}精定位下层";
                SingleAxisMove2Point(_y, "旋转位");
                if (!CheckFlowCondition()) return;
                AxisMoveSimultaneously(new List<Axis>() { _x, _r }, getPos);
                if (!CheckFlowCondition()) return;
                ChangeCylinder.Off();
                SingleAxisMove2Point(_y, getPos);
                SingleCylinderOperate(ChangeCylinder, 5000, false, isNeedLog: true);
                CTMoveEndCalculateNode(out double time);
                MachineMessageScheduler.Instance.Add(new MachineMessage(MessageType.CT, time.ToString("0.0"), "", 0, $"Arm{_headerIndex + 1}到精定位"));
                if (!CheckFlowCondition()) return;
                List<bool> productStatus = new List<bool>();
                if (_headerIndex == 3)
                    productStatus = feedTray.Helper.Select(c => c.Status == CellStatus.HasProduct).Reverse().ToList();
                else
                    productStatus = feedTray.Helper.Select(c => c.Status == CellStatus.HasProduct).ToList();
                if (SystemConfig.Instance.ArmGetPrecisionParam.Count == 0)
                {
                    SystemConfig.Instance.ArmGetPrecisionParam.Add(new NozzleGetParam());
                    SystemConfig.Instance.ArmGetPrecisionParam.Add(new NozzleGetParam());
                    SystemConfig.Instance.ArmGetPrecisionParam.Add(new NozzleGetParam());
                    SystemConfig.Instance.ArmGetPrecisionParam.Add(new NozzleGetParam());

                    SystemConfig.Instance.ArmGetTesterParam.Add(new NozzleGetParam());
                    SystemConfig.Instance.ArmGetTesterParam.Add(new NozzleGetParam());
                    SystemConfig.Instance.ArmGetTesterParam.Add(new NozzleGetParam());
                    SystemConfig.Instance.ArmGetTesterParam.Add(new NozzleGetParam());

                    SystemConfig.Instance.ArmPutPrecisionParam.Add(new NozzlePutParam());
                    SystemConfig.Instance.ArmPutPrecisionParam.Add(new NozzlePutParam());
                    SystemConfig.Instance.ArmPutPrecisionParam.Add(new NozzlePutParam());
                    SystemConfig.Instance.ArmPutPrecisionParam.Add(new NozzlePutParam());

                    SystemConfig.Instance.ArmPutTesterParam.Add(new NozzlePutParam());
                    SystemConfig.Instance.ArmPutTesterParam.Add(new NozzlePutParam());
                    SystemConfig.Instance.ArmPutTesterParam.Add(new NozzlePutParam());
                    SystemConfig.Instance.ArmPutTesterParam.Add(new NozzlePutParam());
                }
                if (true)
                {

                    CTMoveStartCalculateNode();
                    SingleCylinderOperate(upReady ? _precisionPositionUp : _precisionPositionDown, 5000, true);
                    if (!CheckFlowCondition()) return;
                    //精定位定起来
                    NozzleGetProductExtDoE(_z,
                                        _feedCylinders,
                                        _feedVacs,
                                        _feedVacSenses,
                                        vacOther: null,
                                        upReady ? new List<Cylinder>() { _precisionPositionUp } : new List<Cylinder>() { _precisionPositionDown },
                                        checkChangeCylinder: ChangeCylinder,
                                        getPos,
                                        "待机位",
                                        localName: $"{localName}:",
                                        nozzleTimeOut: SystemConfig.Instance.ArmGetPrecisionParam[_headerIndex].NozzleTimeOut,
                                        delayAfterOpenVac: SystemConfig.Instance.ArmGetPrecisionParam[_headerIndex].DelayAfterOpenVac,
                                        delayBeforeVacCheck: SystemConfig.Instance.ArmGetPrecisionParam[_headerIndex].DelayBeforeVacCheck,
                                        GlobalVariable.IsDryRun,
                                        productStatus,
                                        releaseTimeAfter: upReady ? SystemConfig.Instance.UsingStatus.ReleasePrecisionDelayInArmUp : SystemConfig.Instance.UsingStatus.ReleasePrecisionDelayInArmDown,
                                        releaseTimeBefore: upReady ? SystemConfig.Instance.UsingStatus.ReleasePrecisionDelayBeforeInArmUp : SystemConfig.Instance.UsingStatus.ReleasePrecisionDelayBeforeInArmDown);
                    CTMoveEndCalculateNode(out double time1);
                    MachineMessageScheduler.Instance.Add(new MachineMessage(MessageType.CT, time1.ToString("0.0"), "", 0, $"Arm{_headerIndex + 1}取料精定位"));
                }
                else
                {
                    NozzleGetProductExt(_z,
                                        _feedCylinders,
                                        _feedVacs,
                                        _feedVacSenses,
                                        vacOther: null,
                                        null,
                                        getPos,
                                        "待机位",
                                        localName: $"{localName}:",
                                        nozzleTimeOut: SystemConfig.Instance.ArmGetPrecisionParam[_headerIndex].NozzleTimeOut,
                                        delayAfterOpenVac: SystemConfig.Instance.ArmGetPrecisionParam[_headerIndex].DelayAfterOpenVac,
                                        delayBeforeVacCheck: SystemConfig.Instance.ArmGetPrecisionParam[_headerIndex].DelayBeforeVacCheck,
                                        GlobalVariable.IsDryRun,
                                        productStatus);
                }
                if (!CheckFlowCondition()) return;
                //反转回来
                if (_headerIndex == 3)
                    productStatus.Reverse();
                if (_headerIndex < 2)
                {
                    //交换料状态
                    for (int i = 0; i < productStatus.Count; i++)
                    {
                        if (productStatus[i])
                        {
                            _feedHeadTray.Helper.SetCellStatus(i, CellStatus.HasProduct);
                            _feedHeadTray.Helper.SetCellBarcode(i, feedTray.Helper.GetCell(i).SN);
                            _feedHeadTray.Helper.SetCellResult(i, feedTray.Helper.GetCell(i).Result);
                            _feedHeadTray.Helper.GetCell(i).IsUp = upReady;

                            feedTray.Helper.SetCellStatus(i, CellStatus.NoProduct);
                            feedTray.Helper.SetCellBarcode(i, string.Empty);
                            feedTray.Helper.SetCellResult(i, TestResult.NoTest);
                        }
                    }
                }
                else
                {
                    if (_headerIndex == 2)
                    {
                        //交换料状态
                        for (int i = 0; i < productStatus.Count; i++)
                        {
                            if (productStatus[i])
                            {
                                _feedHeadTray.Helper.SetCellStatus(i, CellStatus.HasProduct);
                                _feedHeadTray.Helper.SetCellBarcode(i, feedTray.Helper.GetCell(i).SN);
                                _feedHeadTray.Helper.SetCellResult(i, feedTray.Helper.GetCell(i).Result);
                                _feedHeadTray.Helper.GetCell(i).IsUp = upReady;

                                feedTray.Helper.SetCellStatus(i, CellStatus.NoProduct);
                                feedTray.Helper.SetCellBarcode(i, string.Empty);
                                feedTray.Helper.SetCellResult(i, TestResult.NoTest);
                            }
                        }
                    }
                    else
                    {
                        //交换料状态
                        if (productStatus[0])
                        {
                            _feedHeadTray.Helper.SetCellStatus(1, CellStatus.HasProduct);
                            _feedHeadTray.Helper.SetCellBarcode(1, feedTray.Helper.GetCell(0).SN);
                            _feedHeadTray.Helper.SetCellResult(1, feedTray.Helper.GetCell(0).Result);
                            _feedHeadTray.Helper.GetCell(1).IsUp = upReady;

                            feedTray.Helper.SetCellStatus(0, CellStatus.NoProduct);
                            feedTray.Helper.SetCellBarcode(0, string.Empty);
                            feedTray.Helper.SetCellResult(0, TestResult.NoTest);
                        }
                        if (productStatus[1])
                        {
                            _feedHeadTray.Helper.SetCellStatus(0, CellStatus.HasProduct);
                            _feedHeadTray.Helper.SetCellBarcode(0, feedTray.Helper.GetCell(1).SN);
                            _feedHeadTray.Helper.SetCellResult(0, feedTray.Helper.GetCell(1).Result);
                            _feedHeadTray.Helper.GetCell(0).IsUp = upReady;

                            feedTray.Helper.SetCellStatus(1, CellStatus.NoProduct);
                            feedTray.Helper.SetCellBarcode(1, string.Empty);
                            feedTray.Helper.SetCellResult(1, TestResult.NoTest);
                        }
                    }
                }
                if (isSimulate)
                    Thread.Sleep(2000);
            }
            else
            {
                MachineMessageScheduler.Instance.Add(new MachineMessage(MessageType.Log, $"当前手上已经有料，复测模式下，不需要取料", this.Description, 0, _x.Module));
            }
        }
        public int SelectIdleOrFinishTester(int headerIndex, bool feedProductInHand)
        {
            int idleTesterIndex = -1;
            List<TesterStatus> status = new List<TesterStatus>();
            status.Add((_flowManager.SelectFlow($"测试模组{headerIndex + 1}1") as TesterLogic).CurrentTesterStatus);
            status.Add((_flowManager.SelectFlow($"测试模组{headerIndex + 1}2") as TesterLogic).CurrentTesterStatus);
            status.Add((_flowManager.SelectFlow($"测试模组{headerIndex + 1}3") as TesterLogic).CurrentTesterStatus);
            List<bool> inUse = new List<bool>();
            inUse.Add(SystemConfig.Instance.UsingStatus.TesterInUseStatus[headerIndex * 3 + 0]);
            inUse.Add(SystemConfig.Instance.UsingStatus.TesterInUseStatus[headerIndex * 3 + 1]);
            inUse.Add(SystemConfig.Instance.UsingStatus.TesterInUseStatus[headerIndex * 3 + 2]);
            List<Tray> testerTrays = new List<Tray>();
            testerTrays.Add((FlowManager.Instance.SelectFlow($"测试模组{headerIndex + 1}1") as TesterLogic).TesterTray);
            testerTrays.Add((FlowManager.Instance.SelectFlow($"测试模组{headerIndex + 1}2") as TesterLogic).TesterTray);
            testerTrays.Add((FlowManager.Instance.SelectFlow($"测试模组{headerIndex + 1}3") as TesterLogic).TesterTray);

            for (int i = status.Count - 1; i >= 0; i--)
            {
                //只要有1个tester启用了，就认为整个模组启用
                if (inUse[i] /*|| testerTrays[i].Helper.Any(c => c.Status == CellStatus.HasProduct)*/)
                {
                    if (status[i] == TesterStatus.Idle ||
                        status[i] == TesterStatus.TestFinish)
                    {
                        if (status[i] == TesterStatus.Idle && !feedProductInHand)
                            continue;
                        if (status[i] == TesterStatus.Idle)
                            MachineMessageScheduler.Instance.Add(new MachineMessage(MessageType.Log, $"找到了治具{i + 1}，该治具状态为idle", this.Description, 0, _x.Module));
                        else
                            MachineMessageScheduler.Instance.Add(new MachineMessage(MessageType.Log, $"找到了治具{i + 1}，该治具状态为finish", this.Description, 0, _x.Module));
                        idleTesterIndex = i;
                        break;
                    }
                }
            }
            return idleTesterIndex;
        }
        public int SelectIdleTester()
        {
            int idleTesterIndex = -1;
            List<TesterStatus> status = new List<TesterStatus>();
            status.Add((_flowManager.SelectFlow($"测试模组{_headerIndex + 1}1") as TesterLogic).CurrentTesterStatus);
            status.Add((_flowManager.SelectFlow($"测试模组{_headerIndex + 1}2") as TesterLogic).CurrentTesterStatus);
            status.Add((_flowManager.SelectFlow($"测试模组{_headerIndex + 1}3") as TesterLogic).CurrentTesterStatus);
            List<bool> inUse = new List<bool>();
            inUse.Add(SystemConfig.Instance.UsingStatus.TesterInUseStatus[_headerIndex * 3 + 0]);
            inUse.Add(SystemConfig.Instance.UsingStatus.TesterInUseStatus[_headerIndex * 3 + 1]);
            inUse.Add(SystemConfig.Instance.UsingStatus.TesterInUseStatus[_headerIndex * 3 + 2]);

            for (int i = status.Count - 1; i >= 0; i--)
            {
                //只要有1个tester启用了，就认为整个模组启用
                if (inUse[i])
                {
                    if (status[i] == TesterStatus.Idle)
                    {
                        idleTesterIndex = i;
                        break;
                    }
                }
            }
            return idleTesterIndex;
        }
        public void WaitTesterFinish(int headerIndex, int testerId)
        {
            TesterStatus status = (_flowManager.SelectFlow($"测试模组{headerIndex + 1}{testerId + 1}") as TesterLogic).CurrentTesterStatus;
            while ((status != TesterStatus.TestFinish && status != TesterStatus.Idle) && !IsBreak())
            {
                status = (_flowManager.SelectFlow($"测试模组{headerIndex + 1}{testerId + 1}") as TesterLogic).CurrentTesterStatus;
                Thread.Sleep(20);
            }
        }
        public int SelectFinishTester(string sn)
        {
            int idleTesterIndex = -1;
            List<TesterStatus> status = new List<TesterStatus>();
            status.Add((_flowManager.SelectFlow($"测试模组{_headerIndex + 1}1") as TesterLogic).CurrentTesterStatus);
            status.Add((_flowManager.SelectFlow($"测试模组{_headerIndex + 1}2") as TesterLogic).CurrentTesterStatus);
            status.Add((_flowManager.SelectFlow($"测试模组{_headerIndex + 1}3") as TesterLogic).CurrentTesterStatus);
            List<bool> inUse = new List<bool>();
            inUse.Add(SystemConfig.Instance.UsingStatus.TesterInUseStatus[_headerIndex * 3 + 0]);
            inUse.Add(SystemConfig.Instance.UsingStatus.TesterInUseStatus[_headerIndex * 3 + 1]);
            inUse.Add(SystemConfig.Instance.UsingStatus.TesterInUseStatus[_headerIndex * 3 + 2]);

            for (int i = status.Count - 1; i >= 0; i--)
            {
                //只要有1个tester启用了，就认为整个模组启用
                if (inUse[i])
                {
                    //GlobalVariable.SN2TestCount[sn][i] < GlobalVariable.TesterNo2GrrTimesSingle[i]
                    if ((status[i] == TesterStatus.TestFinish || status[i] == TesterStatus.Idle))
                    {
                        if (!GlobalVariable.SN2FinishStatus.ContainsKey(sn))
                        {
                            if (GlobalVariable.SN2TestCount[sn][i] < GlobalVariable.TesterNo2GrrTimesSingle[i])
                            {
                                idleTesterIndex = i;
                                break;
                            }
                        }
                        else
                        {
                            if (GlobalVariable.SN2TestCount[sn][i] < GlobalVariable.TesterNo2GrrTimesSingle[i] * 2)
                            {
                                idleTesterIndex = i;
                                break;
                            }
                        }
                    }
                }
            }
            return idleTesterIndex;
        }
        public int SelectFinishTester()
        {
            int idleTesterIndex = -1;
            List<TesterStatus> status = new List<TesterStatus>();
            status.Add((_flowManager.SelectFlow($"测试模组{_headerIndex + 1}1") as TesterLogic).CurrentTesterStatus);
            status.Add((_flowManager.SelectFlow($"测试模组{_headerIndex + 1}2") as TesterLogic).CurrentTesterStatus);
            status.Add((_flowManager.SelectFlow($"测试模组{_headerIndex + 1}3") as TesterLogic).CurrentTesterStatus);
            List<bool> inUse = new List<bool>();
            inUse.Add(SystemConfig.Instance.UsingStatus.TesterInUseStatus[_headerIndex * 3 + 0]);
            inUse.Add(SystemConfig.Instance.UsingStatus.TesterInUseStatus[_headerIndex * 3 + 1]);
            inUse.Add(SystemConfig.Instance.UsingStatus.TesterInUseStatus[_headerIndex * 3 + 2]);

            for (int i = status.Count - 1; i >= 0; i--)
            {
                //只要有1个tester启用了，就认为整个模组启用
                if (inUse[i])
                {
                    if (status[i] == TesterStatus.TestFinish)
                    {
                        idleTesterIndex = i;
                        break;
                    }
                }
            }
            return idleTesterIndex;
        }
        public override void InSingleRunMode()
        {
            if (GlobalVariable.OnSingleRunPause != null)
                GlobalVariable.OnSingleRunPause();
        }
        public bool IsTesterIdleOrFinish(int headerIndex, int testerIndex)
        {
            int moduleIndex = headerIndex % 2;
            int tester = testerIndex % 3;
            TesterStatus status = (_flowManager.SelectFlow($"测试模组{moduleIndex + 1}{tester + 1}") as TesterLogic).CurrentTesterStatus;
            if (status == TesterStatus.Idle ||
               status == TesterStatus.TestFinish)
                return true;
            return false;
        }
        /// <summary>
        /// testerIndex: 0 - 2
        /// </summary>
        /// <param name="testerIndex"></param>
        public void GoAndGetPutProduct(int testerIndex)
        {
            SingleAxisMove2Point(_z, "待机位");
            if (!CheckFlowCondition()) return;
            int tester = testerIndex % 3;
            TesterLogic testerLogic = _flowManager.SelectFlow($"测试模组{_headerIndex + 1}{tester + 1}") as TesterLogic;
            bool isChangeUse = false;
            //有料就取料
            if (testerLogic.TesterTray.Helper.Any(c => c.Status == CellStatus.HasProduct))
            {
                isChangeUse = true;
                MachineMessageScheduler.Instance.Add(new MachineMessage(MessageType.Log, $"治具{tester + 1}有完成料可取", this.Description, 0, _x.Module));
                LogHelper.Instance.Log(this.Description, "移动去取完成料开始", MessageLevel.CT);
                CTMoveStartCalculateNode();
                //修改动作顺序，先动Y轴保证 R旋转
                SingleAxisMove2Point(_y, "旋转位");
                if (!CheckFlowCondition()) return;
                bool isUse = false;
                double value = _x.MotionPosition["待机位"];
                Task.Run(() => {
                    while (!isUse)
                    {
                        if (_x.Position<= value)
                        {
                            ChangeCylinder.On();
                            break;
                        }
                        Thread.Sleep(2);
                    }
                });
                AxisMoveSimultaneously(_x, _r, $"治具{_headerIndex + 1}{tester + 1}取料位置");
                isUse = true;
                if (!CheckFlowCondition()) return;
                ChangeCylinder.On();
                SingleAxisMove2Point(_y, $"治具{_headerIndex + 1}{tester + 1}取料位置");
                if (!CheckFlowCondition()) return;
                SingleCylinderOperate(ChangeCylinder, 5000, true, isNeedLog: true);
                if (!CheckFlowCondition()) return;
                CTMoveEndCalculateNode(out double time);
                MachineMessageScheduler.Instance.Add(new MachineMessage(MessageType.CT, time.ToString("0.0"), "", 0, $"Arm{_headerIndex + 1}移动到治具"));
                //************************
                //Check治具使能和Ready状态,确保治具气缸退回去了，才能取料！！！
                //**********************
                CheckTesterStatus(_headerIndex, tester);
                List<bool> cellGetStatus = new List<bool>();
                if (_headerIndex == 2)
                    cellGetStatus = testerLogic.TesterTray.Helper.Select(c => c.Status == CellStatus.HasProduct).ToList();
                else
                    cellGetStatus = testerLogic.TesterTray.Helper.Select(c => c.Status == CellStatus.HasProduct).Reverse().ToList();
                CTMoveStartCalculateNode();
                NozzleGetProductExtWithChangeCylinder(_z,
                                    _recycleCylinders,
                                    _recycleVacs,
                                    _recycleRevVacs,
                                    _recycleVacSenses,
                                    null,
                                    null,
                                    ChangeCylinder,
                                    _vacBreakTesterCylinder[tester],
                                    $"治具{_headerIndex + 1}{tester + 1}取料位置",
                                    "取料后抬起高度",
                                    zHeigth: SystemConfig.Instance.TestParam.ArmZHValue,
                                    zSpeed: SystemConfig.Instance.TestParam.ArmZHValueSpeed,
                                    vacBreakTime: SystemConfig.Instance.TestParam.ArmBreakVacTime,
                                    SystemConfig.Instance.TestParam.ArmGetPWarnVacTime,
                                    nozzleTimeOut: SystemConfig.Instance.ArmGetTesterParam[_headerIndex].NozzleTimeOut,
                                    delayAfterOpenVac: SystemConfig.Instance.ArmGetTesterParam[_headerIndex].DelayAfterOpenVac,
                                    delayBeforeVacCheck: SystemConfig.Instance.ArmGetTesterParam[_headerIndex].DelayBeforeVacCheck,
                                    GlobalVariable.IsDryRun,
                                    cellGetStatus);
                //if (!CheckFlowCondition()) return;
                //SingleCylinderOperate(ChangeCylinder, 5000, false, isNeedLog: true);
                if (!CheckFlowCondition()) return;
                CTMoveEndCalculateNode(out double time1);
                MachineMessageScheduler.Instance.Add(new MachineMessage(MessageType.CT, time1.ToString("0.0"), "", 0, $"Arm{_headerIndex + 1}取料治具"));
                //反转回来
                if (_headerIndex != 2)
                    cellGetStatus.Reverse();
                for (int i = 0; i < 2; i++)
                {
                    if (_headerIndex == 2)
                    {
                        if (cellGetStatus[i])
                        {
                            _recycleHeadTray.Helper.SetCellStatus(i, CellStatus.HasProduct);
                            _recycleHeadTray.Helper.SetCellBarcode(i, testerLogic.TesterTray.Helper.GetCell(i).SN);
                            _recycleHeadTray.Helper.SetCellResult(i, testerLogic.TesterTray.Helper.GetCell(i).Result);
                            _recycleHeadTray.Helper.SetCellTesters(i, testerLogic.TesterTray.Helper.GetCell(i).TestedTesters);

                            testerLogic.TesterTray.Helper.SetCellStatus(i, CellStatus.NoProduct);
                            testerLogic.TesterTray.Helper.SetCellBarcode(i, string.Empty);
                            testerLogic.TesterTray.Helper.SetCellResult(i, TestResult.NoTest);
                        }
                    }
                    else
                    {
                        //3取2穴  4取1穴
                        if (cellGetStatus[i])
                        {
                            if (i == 0)
                            {
                                _recycleHeadTray.Helper.SetCellStatus(1, CellStatus.HasProduct);
                                _recycleHeadTray.Helper.SetCellBarcode(1, testerLogic.TesterTray.Helper.GetCell(0).SN);
                                _recycleHeadTray.Helper.SetCellResult(1, testerLogic.TesterTray.Helper.GetCell(0).Result);
                                _recycleHeadTray.Helper.SetCellTesters(1, testerLogic.TesterTray.Helper.GetCell(0).TestedTesters);

                                testerLogic.TesterTray.Helper.SetCellStatus(0, CellStatus.NoProduct);
                                testerLogic.TesterTray.Helper.SetCellBarcode(0, string.Empty);
                                testerLogic.TesterTray.Helper.SetCellResult(0, TestResult.NoTest);
                            }
                            else
                            {
                                _recycleHeadTray.Helper.SetCellStatus(0, CellStatus.HasProduct);
                                _recycleHeadTray.Helper.SetCellBarcode(0, testerLogic.TesterTray.Helper.GetCell(1).SN);
                                _recycleHeadTray.Helper.SetCellResult(0, testerLogic.TesterTray.Helper.GetCell(1).Result);
                                _recycleHeadTray.Helper.SetCellTesters(0, testerLogic.TesterTray.Helper.GetCell(1).TestedTesters);

                                testerLogic.TesterTray.Helper.SetCellStatus(1, CellStatus.NoProduct);
                                testerLogic.TesterTray.Helper.SetCellBarcode(1, string.Empty);
                                testerLogic.TesterTray.Helper.SetCellResult(1, TestResult.NoTest);
                            }
                        }
                    }
                }
                SetSignal($"取测试模组{_headerIndex + 1}{tester + 1}完成");
                LogHelper.Instance.Log(this.Description, "移动去取完成料结束", MessageLevel.CT);
            }
            //有料就放料
            if (_feedHeadTray.Helper.Any(c => c.Status == CellStatus.HasProduct))
            {
                if (SystemConfig.Instance.UsingStatus.TesterInUseStatus[_headerIndex * 3 + tester])
                {
                    FeedTester(tester, testerLogic,!isChangeUse);
                }
                else
                {
                    MachineMessageScheduler.Instance.Add(new MachineMessage(MessageType.Log, $"治具{tester + 1}被禁用,不进行放料.", this.Description, 0, _x.Module));
                    if (isChangeUse)
                        ChangeCylinder.Off();
                    SingleAxisMove2Point(_z, "待机位");
                    if (!CheckFlowCondition()) return;
                    if (isChangeUse)
                        SingleCylinderOperate(ChangeCylinder, 5000, false, isNeedLog: true);
                }
            }
            else
            {
                if (isChangeUse)
                    ChangeCylinder.Off();
                SingleAxisMove2Point(_z, "待机位");
                if (!CheckFlowCondition()) return;
                if (isChangeUse)
                    SingleCylinderOperate(ChangeCylinder, 5000, false, isNeedLog: true);
            }
        }
        public void CheckTesterStatus(int headerIndex, int testerIndex)
        {
            if (!GlobalVariable.IsDryRun && !SystemConfig.Instance.UsingStatus.IsTesterSimulate)
            {
                TesterLogic testLogic = (FlowManager.Instance.SelectFlow($"测试模组{headerIndex + 1}{testerIndex + 1}") as TesterLogic);
            wait:
                bool enable1Status = testLogic.IsTesterEnabled(headerIndex % 2 * 3 + testerIndex, 0);
                bool enable2Status = testLogic.IsTesterEnabled(headerIndex % 2 * 3 + testerIndex, 1);
                if (!enable1Status)
                {
                    ShowWarnAndPause($"测试模组{headerIndex + 1}治具{testerIndex + 1}Bank0未使能", _x.Module);
                    if (!CheckFlowCondition()) return;
                    goto wait;
                }
                if (!enable2Status)
                {
                    ShowWarnAndPause($"测试模组{headerIndex + 1}治具{testerIndex + 1}Bank1未使能", _x.Module);
                    if (!CheckFlowCondition()) return;
                    goto wait;
                }
                bool ready1Status = testLogic.IsTesterReady(headerIndex % 2 * 3 + testerIndex, 0);
                bool ready2Status = testLogic.IsTesterReady(headerIndex % 2 * 3 + testerIndex, 1);

                if (enable1Status && !ready1Status)
                {
                    ShowWarnAndPause($"测试模组{headerIndex + 1}治具{testerIndex + 1}未Ready,请检查治具!", _x.Module);
                    if (!CheckFlowCondition()) return;
                    goto wait;
                }
                if (enable2Status && !ready2Status)
                {
                    ShowWarnAndPause($"测试模组{headerIndex + 1}治具{testerIndex + 1}未Ready,请检查治具!", _x.Module);
                    if (!CheckFlowCondition()) return;
                    goto wait;
                }
            }
        }
        public void FeedTester(int tester, TesterLogic testerLogic,bool isNeedCheckChange)
        {
            InSingleRunMode();
            //Check治具使能和Ready状态
            CheckTesterStatus(_headerIndex, tester);
            if (!CheckFlowCondition()) return;

            MachineMessageScheduler.Instance.Add(new MachineMessage(MessageType.Log, $"Arm有料可放治具", this.Description, 0, _x.Module));
            LogHelper.Instance.Log(this.Description, "移动去放待测料开始", MessageLevel.CT);
            CTMoveStartCalculateNode();
            SingleAxisMove2Point(_y, "旋转位");
            if (!CheckFlowCondition()) return;
            string putName = "";
            double offsetY = 0;
            int armIndex = _headerIndex + 1;
            int testerIndex = tester + 1;
            bool isUp = true;
            if (_feedHeadTray.Helper.GetCell(0).Status == CellStatus.HasProduct)
            {
                putName = _feedHeadTray.Helper.GetCell(0).IsUp ? $"治具{_headerIndex + 1}{tester + 1}放料位置" : $"治具{_headerIndex + 1}{tester + 1}下层放料位置";
                isUp = _feedHeadTray.Helper.GetCell(0).IsUp;
            }
            else
            {
                putName = _feedHeadTray.Helper.GetCell(1).IsUp ? $"治具{_headerIndex + 1}{tester + 1}放料位置" : $"治具{_headerIndex + 1}{tester + 1}下层放料位置";
                isUp = _feedHeadTray.Helper.GetCell(1).IsUp;
            }
            #region Offset
            if (armIndex == 1)
            {
                if (testerIndex == 1)
                    offsetY = isUp ? SystemConfig.Instance.UsingStatus.TesterPutPUpYArm11 : SystemConfig.Instance.UsingStatus.TesterPutPDownYArm11;
                else if (testerIndex == 2)
                    offsetY = isUp ? SystemConfig.Instance.UsingStatus.TesterPutPUpYArm12 : SystemConfig.Instance.UsingStatus.TesterPutPDownYArm12;
                else if (testerIndex == 3)
                    offsetY = isUp ? SystemConfig.Instance.UsingStatus.TesterPutPUpYArm13 : SystemConfig.Instance.UsingStatus.TesterPutPDownYArm13;
            }
            else if (armIndex == 2)
            {
                if (testerIndex == 1)
                    offsetY = isUp ? SystemConfig.Instance.UsingStatus.TesterPutPUpYArm21 : SystemConfig.Instance.UsingStatus.TesterPutPDownYArm21;
                else if (testerIndex == 2)
                    offsetY = isUp ? SystemConfig.Instance.UsingStatus.TesterPutPUpYArm22 : SystemConfig.Instance.UsingStatus.TesterPutPDownYArm22;
                else if (testerIndex == 3)
                    offsetY = isUp ? SystemConfig.Instance.UsingStatus.TesterPutPUpYArm23 : SystemConfig.Instance.UsingStatus.TesterPutPDownYArm23;
            }
            else if (armIndex == 3)
            {
                if (testerIndex == 1)
                    offsetY = isUp ? SystemConfig.Instance.UsingStatus.TesterPutPUpYArm31 : SystemConfig.Instance.UsingStatus.TesterPutPDownYArm31;
                else if (testerIndex == 2)
                    offsetY = isUp ? SystemConfig.Instance.UsingStatus.TesterPutPUpYArm32 : SystemConfig.Instance.UsingStatus.TesterPutPDownYArm32;
                else if (testerIndex == 3)
                    offsetY = isUp ? SystemConfig.Instance.UsingStatus.TesterPutPUpYArm33 : SystemConfig.Instance.UsingStatus.TesterPutPDownYArm33;
            }
            else if (armIndex == 4)
            {
                if (testerIndex == 1)
                    offsetY = isUp ? SystemConfig.Instance.UsingStatus.TesterPutPUpYArm41 : SystemConfig.Instance.UsingStatus.TesterPutPDownYArm41;
                else if (testerIndex == 2)
                    offsetY = isUp ? SystemConfig.Instance.UsingStatus.TesterPutPUpYArm42 : SystemConfig.Instance.UsingStatus.TesterPutPDownYArm42;
                else if (testerIndex == 3)
                    offsetY = isUp ? SystemConfig.Instance.UsingStatus.TesterPutPUpYArm43 : SystemConfig.Instance.UsingStatus.TesterPutPDownYArm43;
            }
            #endregion
            bool isUse = false;
            if (isNeedCheckChange)
            {
                double value = _x.MotionPosition["待机位"];
                Task.Run(() => {
                    while (!isUse)
                    {
                        if (_x.Position <= value)
                        {
                            ChangeCylinder.On();
                            break;
                        }
                        Thread.Sleep(2);
                    }
                });
            }
            AxisMoveSimultaneously(_x, _r, putName);
            isUse = true;
            if (!CheckFlowCondition()) return;
            ChangeCylinder.On();
            SingleAxisCheckPoint(_y, putName);
            double yPut = _y.MotionPosition[putName] + offsetY;
            SingleAxisMove2Point(_y, yPut);
            SingleCylinderOperate(ChangeCylinder, 5000, true, isNeedLog: true);
            CTMoveEndCalculateNode(out double time);
            MachineMessageScheduler.Instance.Add(new MachineMessage(MessageType.CT, time.ToString("0.0"), "", 0, $"Arm{_headerIndex + 1}移动治具"));
            CTMoveStartCalculateNode();
            if (!CheckFlowCondition()) return;
            List<bool> cellPutStatus = new List<bool>();
            //if(_headerIndex == 2)
            //    cellPutStatus = _feedHeadTray.Helper.Select(c => c.Status == CellStatus.HasProduct).Reverse().ToList();
            //else
            cellPutStatus = _feedHeadTray.Helper.Select(c => c.Status == CellStatus.HasProduct).ToList();

            NozzlePutProductExtWithTester2(_z,
                                           y: _y,
                                          _feedCylinders,
                                          _feedVacs,
                                          _feedRevVacs,
                                          checkChangeCylinder: ChangeCylinder,
                                          testerLogic,
                                          (_headerIndex % 2) * 3 + tester,
                                          zPutPos: putName,
                                          yPutPos: putName,
                                          "待机位",
                                          cellPutStatus,
                                          out bool[] bankVacStatus,
                                          zValRel: SystemConfig.Instance.TestParam.ArmZHValuePut,
                                          zValSpeed: SystemConfig.Instance.TestParam.ArmZHValueSpeedPut,
                                          nozzleTimeOut: SystemConfig.Instance.ArmPutTesterParam[_headerIndex].NozzleTimeOut,
                                          delayBeforeNozzleUp: SystemConfig.Instance.ArmPutTesterParam[_headerIndex].DelayBeforeNozzleUp,
                                          isDryRun: GlobalVariable.IsDryRun || SystemConfig.Instance.UsingStatus.IsTesterSimulate,
                                          isBankReverse: _headerIndex == 2);
            if (!CheckFlowCondition()) return;
            SingleCylinderOperate(ChangeCylinder, 5000, false, isNeedLog: true);
            CTMoveEndCalculateNode(out double time1);
            MachineMessageScheduler.Instance.Add(new MachineMessage(MessageType.CT, time1.ToString("0.0"), "", 0, $"Arm{_headerIndex + 1}放料治具"));
            if (!CheckFlowCondition()) return;
            for (int i = 0; i < cellPutStatus.Count; i++)
            {
                if (_headerIndex == 2)
                {
                    //1放2穴  2放1穴
                    if (cellPutStatus[i])
                    {
                        if (i == 0)
                        {
                            testerLogic.TesterTray.Helper.SetCellStatus(1, CellStatus.HasProduct);
                            testerLogic.TesterTray.Helper.SetCellBarcode(1, _feedHeadTray.Helper.GetCell(0).SN);
                            testerLogic.TesterTray.Helper.SetCellResult(1, _feedHeadTray.Helper.GetCell(0).Result);
                            testerLogic.TesterTray.Helper.SetCellTesters(1, _feedHeadTray.Helper.GetCell(0).TestedTesters);
                            testerLogic.TesterTray.Helper.SetCellPreNG(1, !bankVacStatus[1]);

                            _feedHeadTray.Helper.SetCellStatus(0, CellStatus.NoProduct);
                            _feedHeadTray.Helper.SetCellBarcode(0, string.Empty);
                            _feedHeadTray.Helper.SetCellResult(0, TestResult.NoTest);
                        }
                        else
                        {
                            testerLogic.TesterTray.Helper.SetCellStatus(0, CellStatus.HasProduct);
                            testerLogic.TesterTray.Helper.SetCellBarcode(0, _feedHeadTray.Helper.GetCell(1).SN);
                            testerLogic.TesterTray.Helper.SetCellResult(0, _feedHeadTray.Helper.GetCell(1).Result);
                            testerLogic.TesterTray.Helper.SetCellTesters(0, _feedHeadTray.Helper.GetCell(1).TestedTesters);
                            testerLogic.TesterTray.Helper.SetCellPreNG(0, !bankVacStatus[0]);

                            _feedHeadTray.Helper.SetCellStatus(1, CellStatus.NoProduct);
                            _feedHeadTray.Helper.SetCellBarcode(1, string.Empty);
                            _feedHeadTray.Helper.SetCellResult(1, TestResult.NoTest);
                        }
                    }
                }
                else
                {
                    //1放1穴  2放2穴
                    if (cellPutStatus[i])
                    {
                        testerLogic.TesterTray.Helper.SetCellStatus(i, CellStatus.HasProduct);
                        testerLogic.TesterTray.Helper.SetCellBarcode(i, _feedHeadTray.Helper.GetCell(i).SN);
                        testerLogic.TesterTray.Helper.SetCellResult(i, _feedHeadTray.Helper.GetCell(i).Result);
                        testerLogic.TesterTray.Helper.SetCellTesters(i, _feedHeadTray.Helper.GetCell(i).TestedTesters);
                        testerLogic.TesterTray.Helper.SetCellPreNG(i, !bankVacStatus[i]);

                        _feedHeadTray.Helper.SetCellStatus(i, CellStatus.NoProduct);
                        _feedHeadTray.Helper.SetCellBarcode(i, string.Empty);
                        _feedHeadTray.Helper.SetCellResult(i, TestResult.NoTest);
                    }
                }
            }
            SetSignal($"测试治具{_headerIndex + 1}{tester + 1}开始测试");
            LogHelper.Instance.Log(this.Description, "移动去放待测料结束", MessageLevel.CT);
            InSingleRunMode();
        }
        public void FeedTesterByUnload(int tester, TesterLogic testerLogic)
        {
            //Check治具使能和Ready状态
            CheckTesterStatus(_headerIndex, tester);
            if (!CheckFlowCondition()) return;

            MachineMessageScheduler.Instance.Add(new MachineMessage(MessageType.Log, $"Arm有料可放治具", this.Description, 0, _x.Module));
            LogHelper.Instance.Log(this.Description, "移动去放待测料开始", MessageLevel.CT);
            SingleCylinderOperate(ChangeCylinder, 5000, false, isNeedLog: true);
            if (!CheckFlowCondition()) return;
            SingleAxisMove2Point(_y, "旋转位");
            if (!CheckFlowCondition()) return;
            string putName = "";
            putName = $"治具{_headerIndex + 1}{tester + 1}取料位置";
            AxisMoveSimultaneously(_x, _r, putName);
            if (!CheckFlowCondition()) return;
            ChangeCylinder.On();
            SingleAxisMove2Point(_y, putName);
            SingleCylinderOperate(ChangeCylinder, 5000, true, isNeedLog: true);
            if (!CheckFlowCondition()) return;
            List<bool> cellPutStatus = new List<bool>();
            //if(_headerIndex == 2)
            //    cellPutStatus = _feedHeadTray.Helper.Select(c => c.Status == CellStatus.HasProduct).Reverse().ToList();
            //else
            cellPutStatus = _recycleHeadTray.Helper.Select(c => c.Status == CellStatus.HasProduct).ToList();
            NozzlePutProductExtWithTester2(_z,
                                          _recycleCylinders,
                                          _recycleVacs,
                                          _recycleRevVacs,
                                          checkChangeCylinder: ChangeCylinder,
                                          testerLogic,
                                          (_headerIndex % 2) * 3 + tester,
                                          putName,
                                          "待机位",
                                          cellPutStatus,
                                          out bool[] bankVacStatus,
                                          zValRel: SystemConfig.Instance.TestParam.ArmZHValuePut,
                                          zValSpeed: SystemConfig.Instance.TestParam.ArmZHValueSpeedPut,
                                          nozzleTimeOut: SystemConfig.Instance.ArmPutTesterParam[_headerIndex].NozzleTimeOut,
                                          delayBeforeNozzleUp: SystemConfig.Instance.ArmPutTesterParam[_headerIndex].DelayBeforeNozzleUp,
                                          isDryRun: GlobalVariable.IsDryRun || SystemConfig.Instance.UsingStatus.IsTesterSimulate,
                                          isBankReverse: _headerIndex == 2);
            if (!CheckFlowCondition()) return;
            SingleCylinderOperate(ChangeCylinder, 5000, false, isNeedLog: true);
            if (!CheckFlowCondition()) return;
            for (int i = 0; i < cellPutStatus.Count; i++)
            {
                if (_headerIndex == 2)
                {
                    //1放1穴  2放2穴
                    if (cellPutStatus[i])
                    {
                        testerLogic.TesterTray.Helper.SetCellStatus(i, CellStatus.HasProduct);
                        testerLogic.TesterTray.Helper.SetCellBarcode(i, _recycleHeadTray.Helper.GetCell(i).SN);
                        testerLogic.TesterTray.Helper.SetCellResult(i, _recycleHeadTray.Helper.GetCell(i).Result);
                        testerLogic.TesterTray.Helper.SetCellTesters(i, _recycleHeadTray.Helper.GetCell(i).TestedTesters);

                        _recycleHeadTray.Helper.SetCellStatus(i, CellStatus.NoProduct);
                        _recycleHeadTray.Helper.SetCellBarcode(i, string.Empty);
                        _recycleHeadTray.Helper.SetCellResult(i, TestResult.NoTest);
                    }
                }
                else
                {
                    //1放2穴  2放1穴
                    if (cellPutStatus[i])
                    {
                        if (i == 0)
                        {
                            testerLogic.TesterTray.Helper.SetCellStatus(1, CellStatus.HasProduct);
                            testerLogic.TesterTray.Helper.SetCellBarcode(1, _recycleHeadTray.Helper.GetCell(0).SN);
                            testerLogic.TesterTray.Helper.SetCellResult(1, _recycleHeadTray.Helper.GetCell(0).Result);
                            testerLogic.TesterTray.Helper.SetCellTesters(1, _recycleHeadTray.Helper.GetCell(0).TestedTesters);

                            _recycleHeadTray.Helper.SetCellStatus(0, CellStatus.NoProduct);
                            _recycleHeadTray.Helper.SetCellBarcode(0, string.Empty);
                            _recycleHeadTray.Helper.SetCellResult(0, TestResult.NoTest);
                        }
                        else
                        {
                            testerLogic.TesterTray.Helper.SetCellStatus(0, CellStatus.HasProduct);
                            testerLogic.TesterTray.Helper.SetCellBarcode(0, _recycleHeadTray.Helper.GetCell(1).SN);
                            testerLogic.TesterTray.Helper.SetCellResult(0, _recycleHeadTray.Helper.GetCell(1).Result);
                            testerLogic.TesterTray.Helper.SetCellTesters(0, _recycleHeadTray.Helper.GetCell(1).TestedTesters);

                            _recycleHeadTray.Helper.SetCellStatus(1, CellStatus.NoProduct);
                            _recycleHeadTray.Helper.SetCellBarcode(1, string.Empty);
                            _recycleHeadTray.Helper.SetCellResult(1, TestResult.NoTest);
                        }
                    }
                }
            }
            SetSignal($"测试治具{_headerIndex + 1}{tester + 1}开始测试");
            LogHelper.Instance.Log(this.Description, "移动去放待测料结束", MessageLevel.CT);
        }
        public void NozzlePutProductExtWithTester(Axis z,
                                                  List<Cylinder> nozzle,
                                                  List<Cylinder> vac,
                                                  List<Cylinder> revVac,
                                                  TesterLogic testLogic,
                                                  int testerId,
                                                  string zPutPos,
                                                  string zSafePos,
                                                  List<bool> productStatus,
                                                  int nozzleTimeOut = 5000,
                                                  int delayBeforeNozzleUp = 0,
                                                  string alias = "",
                                                  bool isDryRun = false,
                                                  bool isBankReverse = false)
        {
            DateTime dateTime = DateTime.Now;
            if (isSimulate)
            {
                Thread.Sleep(500);
                return;
            }
        restart:
            if (IsBreak())
                return;
            #region 通知治具开真空
            DateTime timeAfterZNozzleDown = DateTime.Now;
            if (!isDryRun)
            {
                if (isBankReverse)
                {
                    if (productStatus[0])
                    {
                        //只开真空，先不等结果
                        testLogic.OpenVacNoWait(testerId, 1, true, false);
                        //while (!testLogic.OpenVac(testerId, i) && !IsBreak())
                        //{
                        //    ShowWarnAndPause($"{testLogic.Description}治具{testerId}开真空异常", _x.Module);
                        //    if (!CheckFlowCondition()) return;
                        //    Thread.Sleep(20);
                        //}
                        if (!CheckFlowCondition()) return;
                    }
                    if (productStatus[1])
                    {
                        //只开真空，先不等结果
                        testLogic.OpenVacNoWait(testerId, 0, true, false);
                        //while (!testLogic.OpenVac(testerId, i) && !IsBreak())
                        //{
                        //    ShowWarnAndPause($"{testLogic.Description}治具{testerId}开真空异常", _x.Module);
                        //    if (!CheckFlowCondition()) return;
                        //    Thread.Sleep(20);
                        //}
                        if (!CheckFlowCondition()) return;
                    }
                }
                else
                {
                    for (int i = 0; i < productStatus.Count; i++)
                    {
                        if (productStatus[i])
                        {
                            //只开真空，先不等结果
                            testLogic.OpenVacNoWait(testerId, i, true, false);
                            //while (!testLogic.OpenVac(testerId, i) && !IsBreak())
                            //{
                            //    ShowWarnAndPause($"{testLogic.Description}治具{testerId}开真空异常", _x.Module);
                            //    if (!CheckFlowCondition()) return;
                            //    Thread.Sleep(20);
                            //}
                            if (!CheckFlowCondition()) return;
                        }
                    }
                }
            }
            #endregion
            #region 气缸和Z一起下,Z下以后关真空
            SingleAxisMove2Point(z, zPutPos);
            if (!CheckFlowCondition()) return;
            #region 关真空
            if (!isDryRun)
            {
                if (isBankReverse)
                {
                    if (productStatus[0])
                    {
                        //只开真空，先不等结果
                        testLogic.OpenVacNoWait(testerId, 1, false);
                        //while (!testLogic.OpenVac(testerId, i) && !IsBreak())
                        //{
                        //    ShowWarnAndPause($"{testLogic.Description}治具{testerId}开真空异常", _x.Module);
                        //    if (!CheckFlowCondition()) return;
                        //    Thread.Sleep(20);
                        //}
                        if (!CheckFlowCondition()) return;
                    }
                    if (productStatus[1])
                    {
                        //只开真空，先不等结果
                        testLogic.OpenVacNoWait(testerId, 0, false);
                        //while (!testLogic.OpenVac(testerId, i) && !IsBreak())
                        //{
                        //    ShowWarnAndPause($"{testLogic.Description}治具{testerId}开真空异常", _x.Module);
                        //    if (!CheckFlowCondition()) return;
                        //    Thread.Sleep(20);
                        //}
                        if (!CheckFlowCondition()) return;
                    }
                }
                else
                {
                    for (int i = 0; i < productStatus.Count; i++)
                    {
                        if (productStatus[i])
                        {
                            //只开真空，先不等结果
                            testLogic.OpenVacNoWait(testerId, i, false);
                            //while (!testLogic.OpenVac(testerId, i) && !IsBreak())
                            //{
                            //    ShowWarnAndPause($"{testLogic.Description}治具{testerId}开真空异常", _x.Module);
                            //    if (!CheckFlowCondition()) return;
                            //    Thread.Sleep(20);
                            //}
                            if (!CheckFlowCondition()) return;
                        }
                    }
                }
            }
            #endregion
            int index = 0;
            foreach (var nozz in nozzle)
            {
                if (productStatus[index])
                    nozz.On();
                index++;
            }
            Thread.Sleep(400);
            index = 0;
            foreach (var nozz in nozzle)
            {
                if (productStatus[index])
                {
                    if (!nozz.WaitOn(nozzleTimeOut))
                    {
                        MachineMessageScheduler.Instance.Add(new MachineMessage(MessageType.Warn, $"{nozz.Description}到动点{string.Join(",", nozz.MoveNames)}超时", this.Description, 1, nozz.Module));
                        //出错，停下所有线程
                        _flowManager.Pause();
                        //若为报错，需重新检测启动信号
                        WaitForStart();
                        if (!CheckFlowCondition()) return;
                        goto restart;
                    }
                }
                index++;
            }
            if (!CheckFlowCondition()) return;
            #endregion
            #region 关真空，开破真空
            index = 0;
            foreach (var rv in vac)
            {
                if (productStatus[index])
                    rv.Off();
                index++;
            }
            index = 0;
            foreach (var rv in revVac)
            {
                if (productStatus[index])
                    rv.On();
                index++;
            }
            if (!CheckFlowCondition()) return;
            #endregion
            #region 通知治具二次开真空
            if (!isDryRun)
            {
                if (isBankReverse)
                {
                    if (productStatus[0])
                    {
                        //只开真空，先不等结果
                        testLogic.OpenVacNoWait(testerId, 1, true);
                        //while (!testLogic.OpenVac(testerId, i) && !IsBreak())
                        //{
                        //    ShowWarnAndPause($"{testLogic.Description}治具{testerId}开真空异常", _x.Module);
                        //    if (!CheckFlowCondition()) return;
                        //    Thread.Sleep(20);
                        //}
                        if (!CheckFlowCondition()) return;
                    }
                    if (productStatus[1])
                    {
                        //只开真空，先不等结果
                        testLogic.OpenVacNoWait(testerId, 0, true);
                        //while (!testLogic.OpenVac(testerId, i) && !IsBreak())
                        //{
                        //    ShowWarnAndPause($"{testLogic.Description}治具{testerId}开真空异常", _x.Module);
                        //    if (!CheckFlowCondition()) return;
                        //    Thread.Sleep(20);
                        //}
                        if (!CheckFlowCondition()) return;
                    }
                }
                else
                {
                    for (int i = 0; i < productStatus.Count; i++)
                    {
                        if (productStatus[i])
                        {
                            //只开真空，先不等结果
                            testLogic.OpenVacNoWait(testerId, i, true);
                            //while (!testLogic.OpenVac(testerId, i) && !IsBreak())
                            //{
                            //    ShowWarnAndPause($"{testLogic.Description}治具{testerId}开真空异常", _x.Module);
                            //    if (!CheckFlowCondition()) return;
                            //    Thread.Sleep(20);
                            //}
                            if (!CheckFlowCondition()) return;
                        }
                    }
                }
            }
            #endregion
            #region 等待治具真空开启
            if (!isDryRun)
            {
                if (isBankReverse)
                {
                    if (productStatus[0])
                    {
                        //只开真空，先不等结果
                        while (!testLogic.WaitVacOpen(testerId, 1) && !IsBreak())
                        {
                            ShowWarnAndPause($"{testLogic.Description}治具{testerId}开真空异常", _x.Module);
                            if (!CheckFlowCondition()) return;
                            Thread.Sleep(20);
                        }
                        if (!CheckFlowCondition()) return;
                    }
                    if (productStatus[1])
                    {
                        //只开真空，先不等结果
                        while (!testLogic.WaitVacOpen(testerId, 0) && !IsBreak())
                        {
                            ShowWarnAndPause($"{testLogic.Description}治具{testerId}开真空异常", _x.Module);
                            if (!CheckFlowCondition()) return;
                            Thread.Sleep(20);
                            goto restart;
                        }
                        if (!CheckFlowCondition()) return;
                    }
                }
                else
                {
                    for (int i = 0; i < productStatus.Count; i++)
                    {
                        if (productStatus[i])
                        {
                            //只开真空，先不等结果
                            while (!testLogic.WaitVacOpen(testerId, i) && !IsBreak())
                            {
                                ShowWarnAndPause($"{testLogic.Description}治具{testerId}开真空异常", _x.Module);
                                if (!CheckFlowCondition()) return;
                                Thread.Sleep(20);
                                goto restart;
                            }
                            if (!CheckFlowCondition()) return;
                        }
                    }
                }
            }
            #endregion
            DateTime cloaseVacTime = DateTime.Now;
            //抬吸嘴前的延时
            System.Threading.Thread.Sleep(delayBeforeNozzleUp);
            #region 抬吸嘴，抬z轴
            foreach (var nozz in nozzle)
            {
                nozz.Off();
            }
            SingleAxisMove2Point(z, zSafePos);
            if (!CheckFlowCondition()) return;
            foreach (var nozz in nozzle)
            {
                if (!nozz.WaitOff(nozzleTimeOut))
                {
                    MachineMessageScheduler.Instance.Add(new MachineMessage(MessageType.Warn, $"{nozz.Description}到原点{string.Join(",", nozz.MoveNames)}超时", this.Description, 1, nozz.Module));
                    //出错，停下所有线程
                    _flowManager.Pause();
                }
            }
            #endregion
            #region 关破真空
            foreach (var rv in revVac)
            {
                rv.Off();
            }
            if (!CheckFlowCondition()) return;
            #endregion
            #region 时间统计
            DateTime nozzleAndZUpTime = DateTime.Now;
            TimeSpan ts = DateTime.Now - dateTime;
            if (!string.IsNullOrEmpty(alias))
                LogHelper.Instance.Log(this.Description, $"{alias} 总时间: {ts.Milliseconds}ms", MessageLevel.CT);
            else
                LogHelper.Instance.Log(this.Description, $"多吸嘴放料 {string.Join(",", nozzle.Select(n => n.Description).ToArray())} 总时间:{ts.Milliseconds}ms", MessageLevel.CT);

            TimeSpan ts1 = timeAfterZNozzleDown - dateTime;
            if (!string.IsNullOrEmpty(alias))
                LogHelper.Instance.Log(this.Description, $"{alias} 吸嘴&Z下降时间: {ts1.Milliseconds}ms", MessageLevel.CT);
            else
                LogHelper.Instance.Log(this.Description, $"多吸嘴放料 {string.Join(",", nozzle.Select(n => n.Description).ToArray())} 吸嘴&Z下降时间:{ts1.Milliseconds}ms", MessageLevel.CT);

            TimeSpan ts2 = cloaseVacTime - timeAfterZNozzleDown;
            if (!string.IsNullOrEmpty(alias))
                LogHelper.Instance.Log(this.Description, $"{alias} 关真空时间: {ts2.Milliseconds}ms", MessageLevel.CT);
            else
                LogHelper.Instance.Log(this.Description, $"多吸嘴放料 {string.Join(",", nozzle.Select(n => n.Description).ToArray())} 关真空时间:{ts2.Milliseconds}ms", MessageLevel.CT);

            TimeSpan ts3 = nozzleAndZUpTime - cloaseVacTime;
            if (!string.IsNullOrEmpty(alias))
                LogHelper.Instance.Log(this.Description, $"{alias} 吸嘴&Z抬起时间: {ts3.Milliseconds}ms", MessageLevel.CT);
            else
                LogHelper.Instance.Log(this.Description, $"多吸嘴放料 {string.Join(",", nozzle.Select(n => n.Description).ToArray())} 吸嘴&Z抬起时间:{ts3.Milliseconds}ms", MessageLevel.CT);
            #endregion
        }
        public void NozzlePutProductExtWithTester2(Axis z,
                                                   Axis y,
                                                  List<Cylinder> nozzle,
                                                  List<Cylinder> vac,
                                                  List<Cylinder> revVac,
                                                  Cylinder checkChangeCylinder,
                                                  TesterLogic testLogic,
                                                  int testerId,
                                                  string zPutPos,
                                                  string yPutPos,
                                                  string zSafePos,
                                                  List<bool> productStatus,
                                                  out bool[] bankVacStatus,
                                                  double zValRel,
                                                  double zValSpeed,
                                                  int nozzleTimeOut = 5000,
                                                  int delayBeforeNozzleUp = 0,
                                                  string alias = "",
                                                  bool isDryRun = false,
                                                  bool isBankReverse = false)
        {

            InSingleRunMode();
            bankVacStatus = new bool[2] { true, true };

            DateTime dateTime = DateTime.Now;
            if (isSimulate)
            {
                Thread.Sleep(500);
                return;
            }
        restart:
            if (IsBreak())
                return;
            #region 通知治具开真空
            DateTime timeAfterZNozzleDown = DateTime.Now;
            if (!isDryRun)
            {
                if (isBankReverse)
                {
                    if (productStatus[0])
                    {
                        //只开真空，先不等结果
                        testLogic.OpenVacNoWait(testerId, 1, true, true);
                        //while (!testLogic.OpenVac(testerId, i) && !IsBreak())
                        //{
                        //    ShowWarnAndPause($"{testLogic.Description}治具{testerId}开真空异常", _x.Module);
                        //    if (!CheckFlowCondition()) return;
                        //    Thread.Sleep(20);
                        //}
                        if (!CheckFlowCondition()) return;
                    }
                    if (productStatus[1])
                    {
                        //只开真空，先不等结果
                        testLogic.OpenVacNoWait(testerId, 0, true, true);
                        //while (!testLogic.OpenVac(testerId, i) && !IsBreak())
                        //{
                        //    ShowWarnAndPause($"{testLogic.Description}治具{testerId}开真空异常", _x.Module);
                        //    if (!CheckFlowCondition()) return;
                        //    Thread.Sleep(20);
                        //}
                        if (!CheckFlowCondition()) return;
                    }
                }
                else
                {
                    for (int i = 0; i < productStatus.Count; i++)
                    {
                        if (productStatus[i])
                        {
                            //只开真空，先不等结果
                            testLogic.OpenVacNoWait(testerId, i, true, true);
                            //while (!testLogic.OpenVac(testerId, i) && !IsBreak())
                            //{
                            //    ShowWarnAndPause($"{testLogic.Description}治具{testerId}开真空异常", _x.Module);
                            //    if (!CheckFlowCondition()) return;
                            //    Thread.Sleep(20);
                            //}
                            if (!CheckFlowCondition()) return;
                        }
                    }
                }
            }
            #endregion
            #region 气缸和Z一起下
            if (!CheckFlowCondition()) return;
            SingleCylinderWait(checkChangeCylinder, 1000, true);
            SingleAxisMove2Point(z, z.MotionPosition[zPutPos] - zValRel);
            if (!CheckFlowCondition()) return;
            SingleCylinderWait(checkChangeCylinder, 1000, true);
            SingleAxisMove2PointSetSpeed(z, z.MotionPosition[zPutPos], zValSpeed);
            if (!CheckFlowCondition()) return;
            SingleCylinderWait(checkChangeCylinder, 1000, true);
            SingleAxisMove2Point(y, yPutPos);
            if (!CheckFlowCondition()) return;
            int index = 0;
            foreach (var nozz in nozzle)
            {
                if (productStatus[index])
                    nozz.On();
                index++;
            }
            Thread.Sleep(400);
            index = 0;
            foreach (var nozz in nozzle)
            {
                if (productStatus[index])
                {
                    if (!nozz.WaitOn(nozzleTimeOut))
                    {
                        MachineMessageScheduler.Instance.Add(new MachineMessage(MessageType.Warn, $"{nozz.Description}到动点{string.Join(",", nozz.MoveNames)}超时", this.Description, 1, nozz.Module));
                        //出错，停下所有线程
                        _flowManager.Pause();
                        //若为报错，需重新检测启动信号
                        WaitForStart();
                        if (!CheckFlowCondition()) return;
                        goto restart;
                    }
                }
                index++;
            }
            if (!CheckFlowCondition()) return;
            #endregion
            #region 关真空，开破真空
            index = 0;
            foreach (var rv in vac)
            {
                if (productStatus[index])
                    rv.Off();
                index++;
            }
            index = 0;
            foreach (var rv in revVac)
            {
                if (productStatus[index])
                    rv.On();
                index++;
            }
            if (!CheckFlowCondition()) return;
            #endregion
            #region 等待治具真空开启
            if (!isDryRun)
            {
                if (isBankReverse)
                {
                    if (productStatus[0])
                    {
                        bankVacStatus[1] = true;
                        //只开真空，先不等结果
                        while (!testLogic.WaitVacOpen(testerId, 1) && !IsBreak())
                        {
                            nozzle[0].Off();
                            ShowWarnAndPause($"{testLogic.Description}治具{testerId}开真空异常", _x.Module);
                            if (!CheckFlowCondition()) return;
                            if (SystemConfig.Instance.UsingStatus.TesterVacNoCheck)
                            {
                                if (MessageBox.Show("治具真空打开异常，是否重试?若选择 否 ，该产品将不测试并判定为NG", "治具真空", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.No)
                                {
                                    bankVacStatus[1] = false;
                                    break;
                                }
                            }
                            while (!nozzle[0].WaitOn(nozzleTimeOut))
                            {
                                MachineMessageScheduler.Instance.Add(new MachineMessage(MessageType.Warn, $"{nozzle[0].Description}到动点{string.Join(",", nozzle[0].MoveNames)}超时", this.Description, 1, nozzle[0].Module));
                                //出错，停下所有线程
                                _flowManager.Pause();
                                //若为报错，需重新检测启动信号
                                WaitForStart();
                                if (!CheckFlowCondition()) return;
                            }
                            Thread.Sleep(20);
                            if (!CheckFlowCondition()) return;
                        }
                        if (!CheckFlowCondition()) return;
                    }
                    if (productStatus[1])
                    {
                        bankVacStatus[0] = true;
                        //只开真空，先不等结果
                        while (!testLogic.WaitVacOpen(testerId, 0) && !IsBreak())
                        {
                            nozzle[1].Off();
                            ShowWarnAndPause($"{testLogic.Description}治具{testerId}开真空异常", _x.Module);
                            if (!CheckFlowCondition()) return;
                            if (SystemConfig.Instance.UsingStatus.TesterVacNoCheck)
                            {
                                if (MessageBox.Show("治具真空打开异常，是否重试?若选择 否 ，该产品将不测试并判定为NG", "治具真空", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.No)
                                {
                                    bankVacStatus[0] = false;
                                    break;
                                }
                            }
                            while (!nozzle[1].WaitOn(nozzleTimeOut))
                            {
                                MachineMessageScheduler.Instance.Add(new MachineMessage(MessageType.Warn, $"{nozzle[1].Description}到动点{string.Join(",", nozzle[1].MoveNames)}超时", this.Description, 1, nozzle[1].Module));
                                //出错，停下所有线程
                                _flowManager.Pause();
                                //若为报错，需重新检测启动信号
                                WaitForStart();
                                if (!CheckFlowCondition()) return;
                            }
                            Thread.Sleep(20);
                            if (!CheckFlowCondition()) return;
                        }
                        if (!CheckFlowCondition()) return;
                    }
                }
                else
                {
                    bankVacStatus[0] = true;
                    bankVacStatus[1] = true;
                    for (int i = 0; i < productStatus.Count; i++)
                    {
                        if (productStatus[i])
                        {
                            //只开真空，先不等结果
                            while (!testLogic.WaitVacOpen(testerId, i) && !IsBreak())
                            {
                                nozzle[i].Off();
                                ShowWarnAndPause($"{testLogic.Description}治具{testerId}开真空异常", _x.Module);
                                if (!CheckFlowCondition()) return;
                                if (SystemConfig.Instance.UsingStatus.TesterVacNoCheck)
                                {
                                    if (MessageBox.Show("治具真空打开异常，是否重试?若选择 否 ，该产品将不测试并判定为NG", "治具真空", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.No)
                                    {
                                        bankVacStatus[i] = false;
                                        break;
                                    }
                                }
                                while (!nozzle[i].WaitOn(nozzleTimeOut))
                                {
                                    MachineMessageScheduler.Instance.Add(new MachineMessage(MessageType.Warn, $"{nozzle[i].Description}到动点{string.Join(",", nozzle[i].MoveNames)}超时", this.Description, 1, nozzle[i].Module));
                                    //出错，停下所有线程
                                    _flowManager.Pause();
                                    //若为报错，需重新检测启动信号
                                    WaitForStart();
                                    if (!CheckFlowCondition()) return;
                                }
                                Thread.Sleep(20);
                                if (!CheckFlowCondition()) return;
                            }
                            if (!CheckFlowCondition()) return;
                        }
                    }
                }
            }
            #endregion
            DateTime cloaseVacTime = DateTime.Now;
            //抬吸嘴前的延时
            System.Threading.Thread.Sleep(delayBeforeNozzleUp);
            #region 抬吸嘴，抬z轴
            foreach (var nozz in nozzle)
            {
                nozz.Off();
            }
            SingleCylinderWait(checkChangeCylinder, 1000, true);
            bool isChange = false;
            double safeValue = z.MotionPosition[zSafePos] + 100;
            Task.Run(() =>
            {
                while (!isChange)
                {
                    if (z.Position<= safeValue)
                    {
                        checkChangeCylinder.Off();
                        break;
                    }
                    Thread.Sleep(2);
                }
            });
            SingleAxisMove2Point(z, zSafePos);
            isChange = true;
            if (!CheckFlowCondition()) return;
            foreach (var nozz in nozzle)
            {
                if (!nozz.WaitOff(nozzleTimeOut))
                {
                    MachineMessageScheduler.Instance.Add(new MachineMessage(MessageType.Warn, $"{nozz.Description}到原点{string.Join(",", nozz.MoveNames)}超时", this.Description, 1, nozz.Module));
                    //出错，停下所有线程
                    _flowManager.Pause();
                }
            }
            #endregion
            #region 关破真空
            foreach (var rv in revVac)
            {
                rv.Off();
            }
            if (!CheckFlowCondition()) return;
            #endregion
            #region 时间统计
            DateTime nozzleAndZUpTime = DateTime.Now;
            TimeSpan ts = DateTime.Now - dateTime;
            if (!string.IsNullOrEmpty(alias))
                LogHelper.Instance.Log(this.Description, $"{alias} 总时间: {ts.Milliseconds}ms", MessageLevel.CT);
            else
                LogHelper.Instance.Log(this.Description, $"多吸嘴放料 {string.Join(",", nozzle.Select(n => n.Description).ToArray())} 总时间:{ts.Milliseconds}ms", MessageLevel.CT);

            TimeSpan ts1 = timeAfterZNozzleDown - dateTime;
            if (!string.IsNullOrEmpty(alias))
                LogHelper.Instance.Log(this.Description, $"{alias} 吸嘴&Z下降时间: {ts1.Milliseconds}ms", MessageLevel.CT);
            else
                LogHelper.Instance.Log(this.Description, $"多吸嘴放料 {string.Join(",", nozzle.Select(n => n.Description).ToArray())} 吸嘴&Z下降时间:{ts1.Milliseconds}ms", MessageLevel.CT);

            TimeSpan ts2 = cloaseVacTime - timeAfterZNozzleDown;
            if (!string.IsNullOrEmpty(alias))
                LogHelper.Instance.Log(this.Description, $"{alias} 关真空时间: {ts2.Milliseconds}ms", MessageLevel.CT);
            else
                LogHelper.Instance.Log(this.Description, $"多吸嘴放料 {string.Join(",", nozzle.Select(n => n.Description).ToArray())} 关真空时间:{ts2.Milliseconds}ms", MessageLevel.CT);

            TimeSpan ts3 = nozzleAndZUpTime - cloaseVacTime;
            if (!string.IsNullOrEmpty(alias))
                LogHelper.Instance.Log(this.Description, $"{alias} 吸嘴&Z抬起时间: {ts3.Milliseconds}ms", MessageLevel.CT);
            else
                LogHelper.Instance.Log(this.Description, $"多吸嘴放料 {string.Join(",", nozzle.Select(n => n.Description).ToArray())} 吸嘴&Z抬起时间:{ts3.Milliseconds}ms", MessageLevel.CT);
            #endregion

            InSingleRunMode();
        }
        public void NozzlePutProductExtWithTester2(Axis z,
                                               List<Cylinder> nozzle,
                                               List<Cylinder> vac,
                                               List<Cylinder> revVac,
                                               Cylinder checkChangeCylinder,
                                               TesterLogic testLogic,
                                               int testerId,
                                               string zPutPos,
                                               string zSafePos,
                                               List<bool> productStatus,
                                               out bool[] bankVacStatus,
                                               double zValRel,
                                               double zValSpeed,
                                               int nozzleTimeOut = 5000,
                                               int delayBeforeNozzleUp = 0,
                                               string alias = "",
                                               bool isDryRun = false,
                                               bool isBankReverse = false)
        {
            bankVacStatus = new bool[2] { true, true };

            DateTime dateTime = DateTime.Now;
            if (isSimulate)
            {
                Thread.Sleep(500);
                return;
            }
        restart:
            if (IsBreak())
                return;
            #region 通知治具开真空
            DateTime timeAfterZNozzleDown = DateTime.Now;
            if (!isDryRun)
            {
                if (isBankReverse)
                {
                    if (productStatus[0])
                    {
                        //只开真空，先不等结果
                        testLogic.OpenVacNoWait(testerId, 1, true, true);
                        //while (!testLogic.OpenVac(testerId, i) && !IsBreak())
                        //{
                        //    ShowWarnAndPause($"{testLogic.Description}治具{testerId}开真空异常", _x.Module);
                        //    if (!CheckFlowCondition()) return;
                        //    Thread.Sleep(20);
                        //}
                        if (!CheckFlowCondition()) return;
                    }
                    if (productStatus[1])
                    {
                        //只开真空，先不等结果
                        testLogic.OpenVacNoWait(testerId, 0, true, true);
                        //while (!testLogic.OpenVac(testerId, i) && !IsBreak())
                        //{
                        //    ShowWarnAndPause($"{testLogic.Description}治具{testerId}开真空异常", _x.Module);
                        //    if (!CheckFlowCondition()) return;
                        //    Thread.Sleep(20);
                        //}
                        if (!CheckFlowCondition()) return;
                    }
                }
                else
                {
                    for (int i = 0; i < productStatus.Count; i++)
                    {
                        if (productStatus[i])
                        {
                            //只开真空，先不等结果
                            testLogic.OpenVacNoWait(testerId, i, true, true);
                            //while (!testLogic.OpenVac(testerId, i) && !IsBreak())
                            //{
                            //    ShowWarnAndPause($"{testLogic.Description}治具{testerId}开真空异常", _x.Module);
                            //    if (!CheckFlowCondition()) return;
                            //    Thread.Sleep(20);
                            //}
                            if (!CheckFlowCondition()) return;
                        }
                    }
                }
            }
            #endregion
            #region 气缸和Z一起下
            SingleCylinderWait(checkChangeCylinder, 1000, true);
            SingleAxisMove2Point(z, z.MotionPosition[zPutPos] - zValRel);
            if (!CheckFlowCondition()) return;
            SingleCylinderWait(checkChangeCylinder, 1000, true);
            SingleAxisMove2PointSetSpeed(z, z.MotionPosition[zPutPos], zValSpeed);
            if (!CheckFlowCondition()) return;
            int index = 0;
            foreach (var nozz in nozzle)
            {
                if (productStatus[index])
                    nozz.On();
                index++;
            }
            Thread.Sleep(400);
            index = 0;
            foreach (var nozz in nozzle)
            {
                if (productStatus[index])
                {
                    if (!nozz.WaitOn(nozzleTimeOut))
                    {
                        MachineMessageScheduler.Instance.Add(new MachineMessage(MessageType.Warn, $"{nozz.Description}到动点{string.Join(",", nozz.MoveNames)}超时", this.Description, 1, nozz.Module));
                        //出错，停下所有线程
                        _flowManager.Pause();
                        //若为报错，需重新检测启动信号
                        WaitForStart();
                        if (!CheckFlowCondition()) return;
                        goto restart;
                    }
                }
                index++;
            }
            if (!CheckFlowCondition()) return;
            #endregion
            #region 关真空，开破真空
            index = 0;
            foreach (var rv in vac)
            {
                if (productStatus[index])
                    rv.Off();
                index++;
            }
            index = 0;
            foreach (var rv in revVac)
            {
                if (productStatus[index])
                    rv.On();
                index++;
            }
            if (!CheckFlowCondition()) return;
            #endregion
            #region 等待治具真空开启
            if (!isDryRun)
            {
                if (isBankReverse)
                {
                    if (productStatus[0])
                    {
                        bankVacStatus[1] = true;
                        //只开真空，先不等结果
                        while (!testLogic.WaitVacOpen(testerId, 1) && !IsBreak())
                        {
                            nozzle[0].Off();
                            ShowWarnAndPause($"{testLogic.Description}治具{testerId}开真空异常", _x.Module);
                            if (!CheckFlowCondition()) return;
                            if (SystemConfig.Instance.UsingStatus.TesterVacNoCheck)
                            {
                                if (MessageBox.Show("治具真空打开异常，是否重试?若选择 否 ，该产品将不测试并判定为NG", "治具真空", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.No)
                                {
                                    bankVacStatus[1] = false;
                                    break;
                                }
                            }
                            while (!nozzle[0].WaitOn(nozzleTimeOut))
                            {
                                MachineMessageScheduler.Instance.Add(new MachineMessage(MessageType.Warn, $"{nozzle[0].Description}到动点{string.Join(",", nozzle[0].MoveNames)}超时", this.Description, 1, nozzle[0].Module));
                                //出错，停下所有线程
                                _flowManager.Pause();
                                //若为报错，需重新检测启动信号
                                WaitForStart();
                                if (!CheckFlowCondition()) return;
                            }
                            Thread.Sleep(20);
                            if (!CheckFlowCondition()) return;
                        }
                        if (!CheckFlowCondition()) return;
                    }
                    if (productStatus[1])
                    {
                        bankVacStatus[0] = true;
                        //只开真空，先不等结果
                        while (!testLogic.WaitVacOpen(testerId, 0) && !IsBreak())
                        {
                            nozzle[1].Off();
                            ShowWarnAndPause($"{testLogic.Description}治具{testerId}开真空异常", _x.Module);
                            if (!CheckFlowCondition()) return;
                            if (SystemConfig.Instance.UsingStatus.TesterVacNoCheck)
                            {
                                if (MessageBox.Show("治具真空打开异常，是否重试?若选择 否 ，该产品将不测试并判定为NG", "治具真空", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.No)
                                {
                                    bankVacStatus[0] = false;
                                    break;
                                }
                            }
                            while (!nozzle[1].WaitOn(nozzleTimeOut))
                            {
                                MachineMessageScheduler.Instance.Add(new MachineMessage(MessageType.Warn, $"{nozzle[1].Description}到动点{string.Join(",", nozzle[1].MoveNames)}超时", this.Description, 1, nozzle[1].Module));
                                //出错，停下所有线程
                                _flowManager.Pause();
                                //若为报错，需重新检测启动信号
                                WaitForStart();
                                if (!CheckFlowCondition()) return;
                            }
                            Thread.Sleep(20);
                            if (!CheckFlowCondition()) return;
                        }
                        if (!CheckFlowCondition()) return;
                    }
                }
                else
                {
                    bankVacStatus[0] = true;
                    bankVacStatus[1] = true;
                    for (int i = 0; i < productStatus.Count; i++)
                    {
                        if (productStatus[i])
                        {
                            //只开真空，先不等结果
                            while (!testLogic.WaitVacOpen(testerId, i) && !IsBreak())
                            {
                                nozzle[i].Off();
                                ShowWarnAndPause($"{testLogic.Description}治具{testerId}开真空异常", _x.Module);
                                if (!CheckFlowCondition()) return;
                                if (SystemConfig.Instance.UsingStatus.TesterVacNoCheck)
                                {
                                    if (MessageBox.Show("治具真空打开异常，是否重试?若选择 否 ，该产品将不测试并判定为NG", "治具真空", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.No)
                                    {
                                        bankVacStatus[i] = false;
                                        break;
                                    }
                                }
                                while (!nozzle[i].WaitOn(nozzleTimeOut))
                                {
                                    MachineMessageScheduler.Instance.Add(new MachineMessage(MessageType.Warn, $"{nozzle[i].Description}到动点{string.Join(",", nozzle[i].MoveNames)}超时", this.Description, 1, nozzle[i].Module));
                                    //出错，停下所有线程
                                    _flowManager.Pause();
                                    //若为报错，需重新检测启动信号
                                    WaitForStart();
                                    if (!CheckFlowCondition()) return;
                                }
                                Thread.Sleep(20);
                                if (!CheckFlowCondition()) return;
                            }
                            if (!CheckFlowCondition()) return;
                        }
                    }
                }
            }
            #endregion
            DateTime cloaseVacTime = DateTime.Now;
            //抬吸嘴前的延时
            System.Threading.Thread.Sleep(delayBeforeNozzleUp);
            #region 抬吸嘴，抬z轴
            foreach (var nozz in nozzle)
            {
                nozz.Off();
            }
            SingleCylinderWait(checkChangeCylinder, 1000, true);
            SingleAxisMove2Point(z, zSafePos);
            if (!CheckFlowCondition()) return;
            foreach (var nozz in nozzle)
            {
                if (!nozz.WaitOff(nozzleTimeOut))
                {
                    MachineMessageScheduler.Instance.Add(new MachineMessage(MessageType.Warn, $"{nozz.Description}到原点{string.Join(",", nozz.MoveNames)}超时", this.Description, 1, nozz.Module));
                    //出错，停下所有线程
                    _flowManager.Pause();
                }
            }
            #endregion
            #region 关破真空
            foreach (var rv in revVac)
            {
                rv.Off();
            }
            if (!CheckFlowCondition()) return;
            #endregion
            #region 时间统计
            DateTime nozzleAndZUpTime = DateTime.Now;
            TimeSpan ts = DateTime.Now - dateTime;
            if (!string.IsNullOrEmpty(alias))
                LogHelper.Instance.Log(this.Description, $"{alias} 总时间: {ts.Milliseconds}ms", MessageLevel.CT);
            else
                LogHelper.Instance.Log(this.Description, $"多吸嘴放料 {string.Join(",", nozzle.Select(n => n.Description).ToArray())} 总时间:{ts.Milliseconds}ms", MessageLevel.CT);

            TimeSpan ts1 = timeAfterZNozzleDown - dateTime;
            if (!string.IsNullOrEmpty(alias))
                LogHelper.Instance.Log(this.Description, $"{alias} 吸嘴&Z下降时间: {ts1.Milliseconds}ms", MessageLevel.CT);
            else
                LogHelper.Instance.Log(this.Description, $"多吸嘴放料 {string.Join(",", nozzle.Select(n => n.Description).ToArray())} 吸嘴&Z下降时间:{ts1.Milliseconds}ms", MessageLevel.CT);

            TimeSpan ts2 = cloaseVacTime - timeAfterZNozzleDown;
            if (!string.IsNullOrEmpty(alias))
                LogHelper.Instance.Log(this.Description, $"{alias} 关真空时间: {ts2.Milliseconds}ms", MessageLevel.CT);
            else
                LogHelper.Instance.Log(this.Description, $"多吸嘴放料 {string.Join(",", nozzle.Select(n => n.Description).ToArray())} 关真空时间:{ts2.Milliseconds}ms", MessageLevel.CT);

            TimeSpan ts3 = nozzleAndZUpTime - cloaseVacTime;
            if (!string.IsNullOrEmpty(alias))
                LogHelper.Instance.Log(this.Description, $"{alias} 吸嘴&Z抬起时间: {ts3.Milliseconds}ms", MessageLevel.CT);
            else
                LogHelper.Instance.Log(this.Description, $"多吸嘴放料 {string.Join(",", nozzle.Select(n => n.Description).ToArray())} 吸嘴&Z抬起时间:{ts3.Milliseconds}ms", MessageLevel.CT);
            #endregion
        }
    }
    public enum TransferStatus
    {
        TwoPositionNoProduct,
        FeedHasProduct,
        RecycleHasProduct,
    }
}
